Initial commit: is_shoppingcart out of the box v3.0.1
This commit is contained in:
BIN
is_themecore/.DS_Store
vendored
Normal file
BIN
is_themecore/.DS_Store
vendored
Normal file
Binary file not shown.
9
is_themecore/.php-cs-fixer.dist.php
Normal file
9
is_themecore/.php-cs-fixer.dist.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
$config = new PrestaShop\CodingStandards\CsFixer\Config();
|
||||||
|
|
||||||
|
/** @var \Symfony\Component\Finder\Finder $finder */
|
||||||
|
$finder = $config->setUsingCache(true)->getFinder();
|
||||||
|
$finder->in(__DIR__)->exclude('vendor');
|
||||||
|
|
||||||
|
return $config;
|
||||||
674
is_themecore/LICENSE.md
Normal file
674
is_themecore/LICENSE.md
Normal file
@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||||
21
is_themecore/Makefile
Normal file
21
is_themecore/Makefile
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
build-module-zip: build-composer build-assets build-zip
|
||||||
|
|
||||||
|
build-zip:
|
||||||
|
rm -rf is_themecore.zip
|
||||||
|
cp -Ra $(PWD) /tmp/is_themecore
|
||||||
|
rm -rf /tmp/is_themecore/config_*.xml
|
||||||
|
rm -rf /tmp/is_themecore/_theme_dev/node_modules
|
||||||
|
rm -rf /tmp/is_themecore/_module_dev/node_modules
|
||||||
|
rm -rf /tmp/is_themecore/.github
|
||||||
|
rm -rf /tmp/is_themecore/.gitignore
|
||||||
|
rm -rf /tmp/is_themecore/.php-cs-fixer.cache
|
||||||
|
rm -rf /tmp/is_themecore/.git
|
||||||
|
mv -v /tmp/is_themecore $(PWD)/is_themecore
|
||||||
|
zip -r is_themecore.zip is_themecore
|
||||||
|
rm -rf $(PWD)/is_themecore
|
||||||
|
|
||||||
|
build-composer:
|
||||||
|
composer install --no-dev -o
|
||||||
|
|
||||||
|
build-assets:
|
||||||
|
cd _module_dev && . ${HOME}/.nvm/nvm.sh && nvm install && npm install && npm run build
|
||||||
96
is_themecore/README.md
Normal file
96
is_themecore/README.md
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# Theme core module
|
||||||
|
Prestashop module created for [starter theme](https://github.com/Oksydan/modern-prestashop-starter-theme)
|
||||||
|
|
||||||
|
#### How to use assets.yml file
|
||||||
|
|
||||||
|
`assets.yml` file have to be placed inside `themes/THEME_NAME/config/` to work.
|
||||||
|
Example of `assets.yml` file:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
css:
|
||||||
|
product:
|
||||||
|
fileName: product.css
|
||||||
|
priority: 200
|
||||||
|
include:
|
||||||
|
- product
|
||||||
|
checkout:
|
||||||
|
fileName: checkout.css
|
||||||
|
priority: 200
|
||||||
|
include:
|
||||||
|
- cart
|
||||||
|
- order
|
||||||
|
- orderconfirmation
|
||||||
|
blog:
|
||||||
|
fileName: blog.css
|
||||||
|
priority: 200
|
||||||
|
include:
|
||||||
|
- module-blog-*
|
||||||
|
example_remote_bootstrap:
|
||||||
|
fileName: //cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css
|
||||||
|
server: remote # required to set server: remote for remote file
|
||||||
|
priority: 200
|
||||||
|
|
||||||
|
js:
|
||||||
|
product:
|
||||||
|
fileName: product.js
|
||||||
|
priority: 200
|
||||||
|
include:
|
||||||
|
- product
|
||||||
|
checkout:
|
||||||
|
fileName: checkout.js
|
||||||
|
priority: 200
|
||||||
|
include:
|
||||||
|
- cart
|
||||||
|
- order
|
||||||
|
- orderconfirmation
|
||||||
|
blog:
|
||||||
|
fileName: blog.js
|
||||||
|
priority: 200
|
||||||
|
include:
|
||||||
|
- module-blog-*
|
||||||
|
example_remote_bootstrap:
|
||||||
|
fileName: //cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js
|
||||||
|
server: remote # required to set server: remote for remote file
|
||||||
|
priority: 200
|
||||||
|
```
|
||||||
|
|
||||||
|
You are able to use windcard `*` with include page name.
|
||||||
|
|
||||||
|
|
||||||
|
#### Structured data modification
|
||||||
|
|
||||||
|
You are able to modify structured data with hooks. List of hooks:
|
||||||
|
- `actionStructuredDataBreadcrumb`
|
||||||
|
- `actionStructuredDataProduct`
|
||||||
|
- `actionStructuredDataShop`
|
||||||
|
- `actionStructuredDataWebsite`
|
||||||
|
|
||||||
|
Every hook $param is an array with two keys:
|
||||||
|
- `$data` - reference of structured data array
|
||||||
|
- `$rawData` - raw structured data array (provided by data provider)
|
||||||
|
|
||||||
|
#### Partytown
|
||||||
|
|
||||||
|
You are able to use [partytown](https://partytown.builder.io/) with this module. You have to enable it first in module configuration.
|
||||||
|
Example of usage for GTAG:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script>
|
||||||
|
window.partytown.forward.push('datalayer.push');
|
||||||
|
window.partytown.forward.push('gtag');
|
||||||
|
</script>
|
||||||
|
<script type="text/partytown" src="https://www.googletagmanager.com/gtag/js?id=YOUR_GTAG_CODE"></script>
|
||||||
|
<script type="text/partytown">
|
||||||
|
dataLayer = window.dataLayer || [];
|
||||||
|
window.gtag = function () {
|
||||||
|
dataLayer.push(arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.gtag('js', new Date());
|
||||||
|
|
||||||
|
window.gtag('config', 'YOUR_GTAG_CODE');
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Beware that partytown is still in beta, and it may not work as expected. Make sure to test it before using in production.
|
||||||
|
|
||||||
1
is_themecore/_module_dev/.nvmrc
Normal file
1
is_themecore/_module_dev/.nvmrc
Normal file
@ -0,0 +1 @@
|
|||||||
|
v18.12.1
|
||||||
3049
is_themecore/_module_dev/package-lock.json
generated
Normal file
3049
is_themecore/_module_dev/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
is_themecore/_module_dev/package.json
Normal file
20
is_themecore/_module_dev/package.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "is_themecore_module",
|
||||||
|
"label": "Theme core module",
|
||||||
|
"version": "4.0.0",
|
||||||
|
"description": "Theme core module",
|
||||||
|
"author": "Igor Stępień",
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack --progress --mode=production --env stats=errors-only",
|
||||||
|
"js-lint": "eslint -c .eslintrc.js --ext .js,.vue ./src",
|
||||||
|
"js-lint-fix": "eslint -c .eslintrc.js --ext .js,.vue ./src --fix"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
|
"webpack": "^5.81.0",
|
||||||
|
"webpack-cli": "^5.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@builder.io/partytown": "^0.8.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
10
is_themecore/_module_dev/src/index.js
Normal file
10
is_themecore/_module_dev/src/index.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { partytownSnippet } from '@builder.io/partytown/integration';
|
||||||
|
|
||||||
|
const snippetContent = partytownSnippet();
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const script = document.createElement("script");
|
||||||
|
|
||||||
|
script.textContent=snippetContent;
|
||||||
|
document.body.append(script)
|
||||||
|
});
|
||||||
26
is_themecore/_module_dev/webpack.config.js
Normal file
26
is_themecore/_module_dev/webpack.config.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// webpack.config.js
|
||||||
|
const path = require('path');
|
||||||
|
const CopyPlugin = require('copy-webpack-plugin');
|
||||||
|
const partytown = require('@builder.io/partytown/utils');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: {
|
||||||
|
partytown: './src/index.js',
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, '../public'),
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.ts', '.js', '.mjs'],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new CopyPlugin({
|
||||||
|
patterns: [
|
||||||
|
{
|
||||||
|
from: partytown.libDirPath(),
|
||||||
|
to: path.join(__dirname, '../public', '~partytown'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
4
is_themecore/_theme_dev/.eslintignore
Normal file
4
is_themecore/_theme_dev/.eslintignore
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/node_modules/
|
||||||
|
/webpack/
|
||||||
|
postcss.config.js
|
||||||
|
webpack.config.js
|
||||||
27
is_themecore/_theme_dev/.eslintrc.js
Normal file
27
is_themecore/_theme_dev/.eslintrc.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
node: false,
|
||||||
|
es6: true,
|
||||||
|
jquery: true,
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
google: true,
|
||||||
|
document: true,
|
||||||
|
navigator: false,
|
||||||
|
window: true,
|
||||||
|
prestashop: true,
|
||||||
|
},
|
||||||
|
extends: ['airbnb-base'],
|
||||||
|
rules: {
|
||||||
|
'max-len': ['error', {code: 140}],
|
||||||
|
'no-underscore-dangle': 'off',
|
||||||
|
'no-restricted-syntax': 'off',
|
||||||
|
'no-param-reassign': 'off',
|
||||||
|
'import/no-unresolved': 'off',
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 2022
|
||||||
|
},
|
||||||
|
}
|
||||||
1
is_themecore/_theme_dev/.nvmrc
Normal file
1
is_themecore/_theme_dev/.nvmrc
Normal file
@ -0,0 +1 @@
|
|||||||
|
v18.12.1
|
||||||
6
is_themecore/_theme_dev/.stylelintrc.json
Normal file
6
is_themecore/_theme_dev/.stylelintrc.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"extends": "stylelint-config-recommended-scss",
|
||||||
|
"rules": {
|
||||||
|
"scss/at-extend-no-missing-placeholder": null
|
||||||
|
}
|
||||||
|
}
|
||||||
11
is_themecore/_theme_dev/index.php
Normal file
11
is_themecore/_theme_dev/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
21
is_themecore/_theme_dev/package.json
Normal file
21
is_themecore/_theme_dev/package.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "is_themecore",
|
||||||
|
"label": "Theme core module",
|
||||||
|
"version": "4.0.0",
|
||||||
|
"description": "Theme core module",
|
||||||
|
"author": "Igor Stępień",
|
||||||
|
"scripts": {
|
||||||
|
"scss-lint": "stylelint \"**/*.scss\" --fix",
|
||||||
|
"scss-lint-fix": "stylelint \"**/*.scss\" --fix",
|
||||||
|
"js-lint": "eslint -c .eslintrc.js --ext .js,.vue ./src",
|
||||||
|
"js-lint-fix": "eslint -c .eslintrc.js --ext .js,.vue ./src --fix"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"stylelint": "^14.11.0",
|
||||||
|
"stylelint-config-recommended-scss": "^7.0.0",
|
||||||
|
"eslint": "^8.23.0",
|
||||||
|
"eslint-config-airbnb-base": "^15.0.0",
|
||||||
|
"eslint-plugin-import": "^2.26.0",
|
||||||
|
"postcss": "^8.3.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
11
is_themecore/_theme_dev/src/index.php
Normal file
11
is_themecore/_theme_dev/src/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
11
is_themecore/_theme_dev/src/js/index.php
Normal file
11
is_themecore/_theme_dev/src/js/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
58
is_themecore/_theme_dev/src/js/listing/index.js
Normal file
58
is_themecore/_theme_dev/src/js/listing/index.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
function initListDisplay() {
|
||||||
|
const handleClickEvent = (e) => {
|
||||||
|
const target = e.target.closest('[data-toggle-listing]');
|
||||||
|
|
||||||
|
if (target && target.dataset.toggleListing !== undefined) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (target.classList.contains('active')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const display = target.dataset.displayType;
|
||||||
|
const allButtons = document.querySelectorAll('[data-toggle-listing]');
|
||||||
|
|
||||||
|
allButtons.forEach((button) => {
|
||||||
|
button.classList.remove('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
target.classList.add('active');
|
||||||
|
|
||||||
|
let requestData = {
|
||||||
|
displayType: display,
|
||||||
|
ajax: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
requestData = Object.keys(requestData).map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(requestData[key])}`).join('&');
|
||||||
|
|
||||||
|
fetch(window.listDisplayAjaxUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
body: requestData,
|
||||||
|
})
|
||||||
|
.then((resp) => resp.text())
|
||||||
|
.then((resp) => {
|
||||||
|
try {
|
||||||
|
const response = JSON.parse(resp);
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
prestashop.emit('updateFacets', window.location.href);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error); // eslint-disable-line no-console
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error); // eslint-disable-line no-console
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('click', handleClickEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
initListDisplay();
|
||||||
|
});
|
||||||
11
is_themecore/_theme_dev/src/js/listing/index.php
Normal file
11
is_themecore/_theme_dev/src/js/listing/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
2096
is_themecore/_theme_dev/yarn.lock
Normal file
2096
is_themecore/_theme_dev/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
33
is_themecore/composer.json
Normal file
33
is_themecore/composer.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "oksydan/is_themecore",
|
||||||
|
"description": "Theme core module",
|
||||||
|
"type": "prestashop-module",
|
||||||
|
"license": "AFL-3.0",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Igor Stępień",
|
||||||
|
"email": "igor@istpien.dev",
|
||||||
|
"homepage": "https://istpien.dev",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.1.0",
|
||||||
|
"rosell-dk/webp-convert": "^2.9"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"prestashop/php-dev-tools": "^4.2"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Oksydan\\Module\\IsThemeCore\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"prepend-autoloader": false,
|
||||||
|
"allow-plugins": {
|
||||||
|
"phpro/grumphp-shim": false,
|
||||||
|
"phpstan/extension-installer": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2788
is_themecore/composer.lock
generated
Normal file
2788
is_themecore/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
75
is_themecore/config/admin/services.yml
Normal file
75
is_themecore/config/admin/services.yml
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
imports:
|
||||||
|
- { resource: ../common.yml }
|
||||||
|
services:
|
||||||
|
_defaults:
|
||||||
|
public: true
|
||||||
|
|
||||||
|
oksydan.module.is_themecore.form.settings.general_type:
|
||||||
|
class: 'Oksydan\Module\IsThemeCore\Form\Settings\GeneralType'
|
||||||
|
parent: "form.type.translatable.aware"
|
||||||
|
public: true
|
||||||
|
arguments:
|
||||||
|
$displayListChoices: '@=service("oksydan.module.is_themecore.form.choice_provider.list_display_choice_provider").getChoices()'
|
||||||
|
tags:
|
||||||
|
- { name: form.type }
|
||||||
|
|
||||||
|
oksydan.module.is_themecore.form.settings.general_form_data_provider:
|
||||||
|
class: 'Oksydan\Module\IsThemeCore\Form\Settings\GeneralFormDataProvider'
|
||||||
|
arguments:
|
||||||
|
- "@oksydan.module.is_themecore.form.settings.general_configuration"
|
||||||
|
|
||||||
|
oksydan.module.is_themecore.form.settings.general_form_data_handler:
|
||||||
|
class: 'PrestaShop\PrestaShop\Core\Form\Handler'
|
||||||
|
arguments:
|
||||||
|
- "@form.factory"
|
||||||
|
- "@prestashop.core.hook.dispatcher"
|
||||||
|
- "@oksydan.module.is_themecore.form.settings.general_form_data_provider"
|
||||||
|
- 'Oksydan\Module\IsThemeCore\Form\Settings\GeneralType'
|
||||||
|
- "General"
|
||||||
|
|
||||||
|
oksydan.module.is_themecore.form.settings.webp_type:
|
||||||
|
class: 'Oksydan\Module\IsThemeCore\Form\Settings\WebpType'
|
||||||
|
parent: "form.type.translatable.aware"
|
||||||
|
public: true
|
||||||
|
arguments:
|
||||||
|
$convertersList: '@=service("oksydan.module.is_themecore.form.choice_provider.webp_library_choice_provider").getChoices()'
|
||||||
|
$convertersListFull: '@=service("oksydan.module.is_themecore.form.choice_provider.webp_library_choice_provider").getChoicesFull()'
|
||||||
|
$router: "@router"
|
||||||
|
tags:
|
||||||
|
- { name: form.type }
|
||||||
|
|
||||||
|
oksydan.module.is_themecore.form.settings.webp_form_data_provider:
|
||||||
|
class: 'Oksydan\Module\IsThemeCore\Form\Settings\WebpFormDataProvider'
|
||||||
|
arguments:
|
||||||
|
- "@oksydan.module.is_themecore.form.settings.webp_configuration"
|
||||||
|
|
||||||
|
oksydan.module.is_themecore.form.settings.webp_form_data_handler:
|
||||||
|
class: 'PrestaShop\PrestaShop\Core\Form\Handler'
|
||||||
|
arguments:
|
||||||
|
- "@form.factory"
|
||||||
|
- "@prestashop.core.hook.dispatcher"
|
||||||
|
- "@oksydan.module.is_themecore.form.settings.webp_form_data_provider"
|
||||||
|
- 'Oksydan\Module\IsThemeCore\Form\Settings\WebpType'
|
||||||
|
- "Webp"
|
||||||
|
|
||||||
|
oksydan.module.is_themecore.form.choice_provider.webp_library_choice_provider:
|
||||||
|
class: Oksydan\Module\IsThemeCore\Form\ChoiceProvider\WebpLibraryChoiceProvider
|
||||||
|
arguments:
|
||||||
|
- "@oksydan.module.is_themecore.core.webp.webp_convert_libraries"
|
||||||
|
|
||||||
|
oksydan.module.is_themecore.form.choice_provider.list_display_choice_provider:
|
||||||
|
class: Oksydan\Module\IsThemeCore\Form\ChoiceProvider\ListDisplayChoiceProvider
|
||||||
|
arguments:
|
||||||
|
- "@oksydan.module.is_themecore.core.listing_display.theme_list_display"
|
||||||
|
|
||||||
|
oksydan.module.is_themecore.form.settings.webp_configuration:
|
||||||
|
class: Oksydan\Module\IsThemeCore\Form\Settings\WebpConfiguration
|
||||||
|
arguments:
|
||||||
|
- "@prestashop.adapter.legacy.configuration"
|
||||||
|
|
||||||
|
oksydan.module.is_themecore.form.settings.general_configuration:
|
||||||
|
class: Oksydan\Module\IsThemeCore\Form\Settings\GeneralConfiguration
|
||||||
|
arguments:
|
||||||
|
- "@prestashop.adapter.legacy.configuration"
|
||||||
|
- "@prestashop.adapter.shop.context"
|
||||||
|
- "@prestashop.adapter.multistore_feature"
|
||||||
39
is_themecore/config/common.yml
Normal file
39
is_themecore/config/common.yml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
services:
|
||||||
|
_defaults:
|
||||||
|
public: true
|
||||||
|
|
||||||
|
oksydan.module.is_themecore.module:
|
||||||
|
class: Is_themecore
|
||||||
|
factory: [ 'Module', 'getInstanceByName' ]
|
||||||
|
public: false
|
||||||
|
arguments:
|
||||||
|
- 'is_themecore'
|
||||||
|
|
||||||
|
Is_themecore: '@oksydan.module.is_themecore.module'
|
||||||
|
|
||||||
|
oksydan.module.is_themecore.core.webp.webp_convert_libraries:
|
||||||
|
class: Oksydan\Module\IsThemeCore\Core\Webp\WebpConvertLibraries
|
||||||
|
|
||||||
|
oksydan.module.is_themecore.core.webp.webp_files_eraser:
|
||||||
|
class: Oksydan\Module\IsThemeCore\Core\Webp\WebpFilesEraser
|
||||||
|
|
||||||
|
oksydan.module.is_themecore.core.listing_display.theme_list_display:
|
||||||
|
class: Oksydan\Module\IsThemeCore\Core\ListingDisplay\ThemeListDisplay
|
||||||
|
|
||||||
|
oksydan.module.is_themecore.core.htaccess.htaccess_generator:
|
||||||
|
class: Oksydan\Module\IsThemeCore\Core\Htaccess\HtaccessGenerator
|
||||||
|
arguments:
|
||||||
|
- "@Is_themecore"
|
||||||
|
|
||||||
|
oksydan.module.is_themecore.core.webp.related_image_file_finder:
|
||||||
|
class: Oksydan\Module\IsThemeCore\Core\Webp\RelatedImageFileFinder
|
||||||
|
|
||||||
|
oksydan.module.is_themecore.core.webp.webp_generator:
|
||||||
|
class: Oksydan\Module\IsThemeCore\Core\Webp\WebpGenerator
|
||||||
|
arguments:
|
||||||
|
- "@oksydan.module.is_themecore.core.webp.related_image_file_finder"
|
||||||
|
|
||||||
|
Oksydan\Module\IsThemeCore\Core\Partytown\FilesInstallation:
|
||||||
|
class: Oksydan\Module\IsThemeCore\Core\Partytown\FilesInstallation
|
||||||
|
arguments:
|
||||||
|
- "@Is_themecore"
|
||||||
81
is_themecore/config/front/services.yml
Normal file
81
is_themecore/config/front/services.yml
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
imports:
|
||||||
|
- { resource: ../common.yml }
|
||||||
|
services:
|
||||||
|
_defaults:
|
||||||
|
public: true
|
||||||
|
|
||||||
|
Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataBreadcrumbPresenter:
|
||||||
|
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataBreadcrumbPresenter'
|
||||||
|
|
||||||
|
Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataBreadcrumbProvider:
|
||||||
|
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataBreadcrumbProvider'
|
||||||
|
arguments:
|
||||||
|
- "@=service('prestashop.adapter.legacy.context').getContext()"
|
||||||
|
|
||||||
|
Oksydan\Module\IsThemeCore\Core\StructuredData\BreadcrumbStructuredData:
|
||||||
|
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\BreadcrumbStructuredData'
|
||||||
|
public: true
|
||||||
|
arguments:
|
||||||
|
- '@Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataBreadcrumbProvider'
|
||||||
|
- '@Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataBreadcrumbPresenter'
|
||||||
|
|
||||||
|
Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataWebsitePresenter:
|
||||||
|
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataWebsitePresenter'
|
||||||
|
arguments:
|
||||||
|
- "@=service('prestashop.adapter.legacy.context').getContext()"
|
||||||
|
|
||||||
|
Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataWebsiteProvider:
|
||||||
|
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataWebsiteProvider'
|
||||||
|
arguments:
|
||||||
|
- "@=service('prestashop.adapter.legacy.context').getContext()"
|
||||||
|
|
||||||
|
Oksydan\Module\IsThemeCore\Core\StructuredData\WebsiteStructuredData:
|
||||||
|
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\WebsiteStructuredData'
|
||||||
|
public: true
|
||||||
|
arguments:
|
||||||
|
- '@Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataWebsiteProvider'
|
||||||
|
- '@Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataWebsitePresenter'
|
||||||
|
|
||||||
|
Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataShopPresenter:
|
||||||
|
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataShopPresenter'
|
||||||
|
arguments:
|
||||||
|
- "@=service('prestashop.adapter.legacy.context').getContext()"
|
||||||
|
|
||||||
|
Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataShopProvider:
|
||||||
|
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataShopProvider'
|
||||||
|
arguments:
|
||||||
|
- "@=service('prestashop.adapter.legacy.context').getContext()"
|
||||||
|
|
||||||
|
Oksydan\Module\IsThemeCore\Core\StructuredData\ShopStructuredData:
|
||||||
|
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\ShopStructuredData'
|
||||||
|
public: true
|
||||||
|
arguments:
|
||||||
|
- '@Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataShopProvider'
|
||||||
|
- '@Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataShopPresenter'
|
||||||
|
|
||||||
|
Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataProductPresenter:
|
||||||
|
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataProductPresenter'
|
||||||
|
arguments:
|
||||||
|
- "@=service('prestashop.adapter.legacy.context').getContext()"
|
||||||
|
|
||||||
|
Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataProductProvider:
|
||||||
|
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataProductProvider'
|
||||||
|
arguments:
|
||||||
|
- "@=service('prestashop.adapter.legacy.context').getContext()"
|
||||||
|
|
||||||
|
Oksydan\Module\IsThemeCore\Core\StructuredData\ProductStructuredData:
|
||||||
|
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\ProductStructuredData'
|
||||||
|
public: true
|
||||||
|
arguments:
|
||||||
|
- '@Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataProductProvider'
|
||||||
|
- '@Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataProductPresenter'
|
||||||
|
|
||||||
|
Oksydan\Module\IsThemeCore\Core\Partytown\PartytownScript:
|
||||||
|
class: Oksydan\Module\IsThemeCore\Core\Partytown\PartytownScript
|
||||||
|
arguments:
|
||||||
|
- "@Is_themecore"
|
||||||
|
|
||||||
|
Oksydan\Module\IsThemeCore\Core\Partytown\PartytownScriptUriResolver:
|
||||||
|
class: Oksydan\Module\IsThemeCore\Core\Partytown\PartytownScriptUriResolver
|
||||||
|
arguments:
|
||||||
|
- "@=service('prestashop.adapter.legacy.context').getContext()"
|
||||||
11
is_themecore/config/index.php
Normal file
11
is_themecore/config/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
30
is_themecore/config/routes.yml
Normal file
30
is_themecore/config/routes.yml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
is_themecore_module_settings:
|
||||||
|
path: /is_themecore/settings
|
||||||
|
methods: [GET]
|
||||||
|
defaults:
|
||||||
|
_controller: 'Oksydan\Module\IsThemeCore\Controller\Admin\SettingsController::indexAction'
|
||||||
|
_legacy_controller: themecoreSettings
|
||||||
|
|
||||||
|
is_themecore_module_settings_general_save:
|
||||||
|
path: /is_themecore/settings/general
|
||||||
|
methods: [POST, PATCH]
|
||||||
|
defaults:
|
||||||
|
_controller: 'Oksydan\Module\IsThemeCore\Controller\Admin\SettingsController::processGeneralFormAction'
|
||||||
|
_legacy_controller: themecoreSettings
|
||||||
|
_legacy_link: themecoreSettings:update
|
||||||
|
|
||||||
|
is_themecore_module_settings_webp_save:
|
||||||
|
path: /is_themecore/settings/webp
|
||||||
|
methods: [POST, PATCH]
|
||||||
|
defaults:
|
||||||
|
_controller: 'Oksydan\Module\IsThemeCore\Controller\Admin\SettingsController::processWebpFormAction'
|
||||||
|
_legacy_controller: themecoreSettings
|
||||||
|
_legacy_link: themecoreSettings:update
|
||||||
|
|
||||||
|
is_themecore_module_settings_webp_erase_all:
|
||||||
|
path: /is_themecore/settings/webp
|
||||||
|
methods: [GET]
|
||||||
|
defaults:
|
||||||
|
_controller: 'Oksydan\Module\IsThemeCore\Controller\Admin\SettingsController::processWebpEraseImages'
|
||||||
|
_legacy_controller: themecoreSettings
|
||||||
|
_legacy_link: themecoreSettings:update
|
||||||
35
is_themecore/controllers/front/ajaxTheme.php
Normal file
35
is_themecore/controllers/front/ajaxTheme.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Oksydan\Module\IsThemeCore\Core\ListingDisplay\ThemeListDisplay;
|
||||||
|
|
||||||
|
class Is_themecoreAjaxThemeModuleFrontController extends ModuleFrontController
|
||||||
|
{
|
||||||
|
public $displayType;
|
||||||
|
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
|
||||||
|
$this->displayType = Tools::getValue('displayType');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initContent()
|
||||||
|
{
|
||||||
|
parent::initContent();
|
||||||
|
|
||||||
|
$themeDisplay = new ThemeListDisplay();
|
||||||
|
|
||||||
|
if ($this->displayType) {
|
||||||
|
$themeDisplay->setDisplay($this->displayType);
|
||||||
|
$this->ajaxRender(json_encode([
|
||||||
|
'hasError' => false,
|
||||||
|
'success' => true,
|
||||||
|
]));
|
||||||
|
} else {
|
||||||
|
$this->ajaxRender(json_encode([
|
||||||
|
'hasError' => true,
|
||||||
|
'success' => false,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
is_themecore/controllers/front/index.php
Normal file
11
is_themecore/controllers/front/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
11
is_themecore/controllers/index.php
Normal file
11
is_themecore/controllers/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
11
is_themecore/index.php
Normal file
11
is_themecore/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
208
is_themecore/is_themecore.php
Normal file
208
is_themecore/is_themecore.php
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
if (file_exists(dirname(__FILE__) . '/vendor/autoload.php')) {
|
||||||
|
require_once dirname(__FILE__) . '/vendor/autoload.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
use Oksydan\Module\IsThemeCore\Core\Partytown\FilesInstallation;
|
||||||
|
use Oksydan\Module\IsThemeCore\Form\Settings\GeneralConfiguration;
|
||||||
|
use Oksydan\Module\IsThemeCore\Form\Settings\WebpConfiguration;
|
||||||
|
use Oksydan\Module\IsThemeCore\HookDispatcher;
|
||||||
|
use PrestaShop\PrestaShop\Adapter\Configuration;
|
||||||
|
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
|
||||||
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
|
|
||||||
|
class Is_themecore extends Module
|
||||||
|
{
|
||||||
|
protected $hookDispatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, string> Configuration values
|
||||||
|
*/
|
||||||
|
public const CONFIGURATION_VALUES = [
|
||||||
|
GeneralConfiguration::THEMECORE_DISPLAY_LIST => 'grid',
|
||||||
|
GeneralConfiguration::THEMECORE_EARLY_HINTS => false,
|
||||||
|
GeneralConfiguration::THEMECORE_PRELOAD_CSS => false,
|
||||||
|
GeneralConfiguration::THEMECORE_LOAD_PARTY_TOWN => false,
|
||||||
|
GeneralConfiguration::THEMECORE_DEBUG_PARTY_TOWN => false,
|
||||||
|
WebpConfiguration::THEMECORE_WEBP_ENABLED => false,
|
||||||
|
WebpConfiguration::THEMECORE_WEBP_QUALITY => 90,
|
||||||
|
WebpConfiguration::THEMECORE_WEBP_CONVERTER => null,
|
||||||
|
WebpConfiguration::THEMECORE_WEBP_SHARPYUV => false,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[] Hooks to register
|
||||||
|
*/
|
||||||
|
public const HOOKS = [
|
||||||
|
'actionOutputHTMLBefore',
|
||||||
|
'actionDispatcherBefore',
|
||||||
|
'actionFrontControllerSetMedia',
|
||||||
|
'displayListingStructuredData',
|
||||||
|
'displayHeader',
|
||||||
|
'actionProductSearchAfter',
|
||||||
|
'actionHtaccessCreate',
|
||||||
|
'objectShopUrlAddAfter',
|
||||||
|
'objectShopUrlUpdateAfter',
|
||||||
|
'objectShopUrlDeleteAfter',
|
||||||
|
'actionFrontControllerInitBefore',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Configuration<string, mixed> Configuration
|
||||||
|
*/
|
||||||
|
private $configuration;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->name = 'is_themecore';
|
||||||
|
$this->tab = 'others';
|
||||||
|
$this->version = '4.1.3';
|
||||||
|
$this->author = 'Igor Stępień';
|
||||||
|
$this->ps_versions_compliancy = ['min' => '8.0.0', 'max' => _PS_VERSION_];
|
||||||
|
|
||||||
|
$this->bootstrap = true;
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->displayName = $this->trans(
|
||||||
|
'Theme core module',
|
||||||
|
[],
|
||||||
|
'Modules.isthemecore.Admin'
|
||||||
|
);
|
||||||
|
$this->description = $this->trans(
|
||||||
|
'Required for theme to work.',
|
||||||
|
[],
|
||||||
|
'Modules.isthemecore.Admin'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->tabs = [
|
||||||
|
[
|
||||||
|
'name' => 'Theme core module settings',
|
||||||
|
'class_name' => 'themecoreSettings',
|
||||||
|
'route_name' => 'is_themecore_module_settings',
|
||||||
|
'parent_class_name' => 'CONFIGURE',
|
||||||
|
'visible' => false,
|
||||||
|
'wording' => 'Theme core module settings',
|
||||||
|
'wording_domain' => 'Modules.isthemecore.Admin',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->configuration = new Configuration();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function isUsingNewTranslationSystem(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function install(): bool
|
||||||
|
{
|
||||||
|
return parent::install()
|
||||||
|
&& $this->installConfiguration()
|
||||||
|
&& $this->installPartytown()
|
||||||
|
&& $this->registerHook(self::HOOKS)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install configuration values
|
||||||
|
*/
|
||||||
|
private function installConfiguration(): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
foreach (self::CONFIGURATION_VALUES as $key => $default_value) {
|
||||||
|
$this->configuration->set($key, $default_value);
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install Partytown
|
||||||
|
*/
|
||||||
|
private function installPartytown(): bool
|
||||||
|
{
|
||||||
|
$installer = new FilesInstallation($this); // SERVICES NOT AVAILABLE DURING INSTALLATION
|
||||||
|
|
||||||
|
try {
|
||||||
|
$installer->installFiles();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function uninstall(): bool
|
||||||
|
{
|
||||||
|
return parent::uninstall()
|
||||||
|
&& $this->uninstallConfiguration()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uninstall configuration values
|
||||||
|
*/
|
||||||
|
private function uninstallConfiguration(): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
foreach (array_keys(self::CONFIGURATION_VALUES) as $key) {
|
||||||
|
$this->configuration->remove($key);
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get module configuration page content
|
||||||
|
*/
|
||||||
|
public function getContent(): void
|
||||||
|
{
|
||||||
|
$container = SymfonyContainer::getInstance();
|
||||||
|
|
||||||
|
if ($container != null) {
|
||||||
|
/** @var UrlGeneratorInterface */
|
||||||
|
$router = $container->get('router');
|
||||||
|
|
||||||
|
Tools::redirectAdmin($router->generate('is_themecore_module_settings'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call(string $methodName, array $arguments)
|
||||||
|
{
|
||||||
|
return $this
|
||||||
|
->getHookDispatcher()
|
||||||
|
->dispatch($methodName, $arguments[0] ?? []);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getHookDispatcher(): HookDispatcher
|
||||||
|
{
|
||||||
|
if (!isset($this->hookDispatcher)) {
|
||||||
|
if (!class_exists(HookDispatcher::class)) {
|
||||||
|
require_once dirname(__FILE__) . '/vendor/autoload.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->hookDispatcher = new HookDispatcher($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->hookDispatcher;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
is_themecore/logo.png
Normal file
BIN
is_themecore/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
206
is_themecore/src/Controller/Admin/SettingsController.php
Normal file
206
is_themecore/src/Controller/Admin/SettingsController.php
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Controller\Admin;
|
||||||
|
|
||||||
|
use PrestaShop\PrestaShop\Core\Form\FormHandlerInterface;
|
||||||
|
use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController;
|
||||||
|
use PrestaShopBundle\Security\Annotation\AdminSecurity;
|
||||||
|
use PrestaShopBundle\Security\Annotation\DemoRestricted;
|
||||||
|
use PrestaShopBundle\Security\Annotation\ModuleActivated;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class SettingsController
|
||||||
|
*
|
||||||
|
* @ModuleActivated(moduleName="is_themecore", redirectRoute="admin_module_manage")
|
||||||
|
*/
|
||||||
|
class SettingsController extends FrameworkBundleAdminController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @AdminSecurity(
|
||||||
|
* "is_granted(['read'], request.get('_legacy_controller'))",
|
||||||
|
* message="You do not have permission to access this."
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function indexAction(Request $request): Response
|
||||||
|
{
|
||||||
|
$generalFormDataHandler = $this->getGeneralFormHandler();
|
||||||
|
$webpFormDataHandler = $this->getWebpFormHandler();
|
||||||
|
|
||||||
|
/** @var FormInterface<string, mixed> $generalForm */
|
||||||
|
$generalForm = $generalFormDataHandler->getForm();
|
||||||
|
$webpForm = $webpFormDataHandler->getForm();
|
||||||
|
|
||||||
|
return $this->render('@Modules/is_themecore/views/templates/back/components/layouts/settings.html.twig', [
|
||||||
|
'general_form' => $generalForm->createView(),
|
||||||
|
'webp_form' => $webpForm->createView(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @AdminSecurity(
|
||||||
|
* "is_granted('update', request.get('_legacy_controller')) && is_granted('create', request.get('_legacy_controller')) && is_granted('delete', request.get('_legacy_controller'))",
|
||||||
|
* message="You do not have permission to update this.",
|
||||||
|
* redirectRoute="is_themecore_module_settings"
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* @DemoRestricted(redirectRoute="is_themecore_module_settings")
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return RedirectResponse
|
||||||
|
*
|
||||||
|
* @throws \LogicException
|
||||||
|
*/
|
||||||
|
public function processGeneralFormAction(Request $request)
|
||||||
|
{
|
||||||
|
return $this->processForm(
|
||||||
|
$request,
|
||||||
|
$this->getGeneralFormHandler(),
|
||||||
|
'General'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @AdminSecurity(
|
||||||
|
* "is_granted('update', request.get('_legacy_controller')) && is_granted('create', request.get('_legacy_controller')) && is_granted('delete', request.get('_legacy_controller'))",
|
||||||
|
* message="You do not have permission to update this.",
|
||||||
|
* redirectRoute="is_themecore_module_settings"
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* @DemoRestricted(redirectRoute="is_themecore_module_settings")
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return RedirectResponse
|
||||||
|
*
|
||||||
|
* @throws \LogicException
|
||||||
|
*/
|
||||||
|
public function processWebpFormAction(Request $request)
|
||||||
|
{
|
||||||
|
return $this->processForm(
|
||||||
|
$request,
|
||||||
|
$this->getWebpFormHandler(),
|
||||||
|
'Webp'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @DemoRestricted(redirectRoute="is_themecore_module_settings")
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* @return RedirectResponse
|
||||||
|
*
|
||||||
|
* @throws \LogicException
|
||||||
|
*/
|
||||||
|
public function processWebpEraseImages(Request $request)
|
||||||
|
{
|
||||||
|
$time_start = microtime(true);
|
||||||
|
$eraser = $this->get('oksydan.module.is_themecore.core.webp.webp_files_eraser');
|
||||||
|
|
||||||
|
switch ($request->get('type')) {
|
||||||
|
case 'all':
|
||||||
|
$eraser->setQuery(_PS_ROOT_DIR_);
|
||||||
|
break;
|
||||||
|
case 'product':
|
||||||
|
$eraser->setQuery(_PS_PROD_IMG_DIR_);
|
||||||
|
break;
|
||||||
|
case 'module':
|
||||||
|
$eraser->setQuery(_PS_MODULE_DIR_);
|
||||||
|
break;
|
||||||
|
case 'cms':
|
||||||
|
$eraser->setQuery(_PS_IMG_DIR_ . 'cms/');
|
||||||
|
break;
|
||||||
|
case 'themes':
|
||||||
|
$eraser->setQuery(_PS_ROOT_DIR_ . '/themes/');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$eraser->setQuery(_PS_ROOT_DIR_);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$eraser->eraseFiles();
|
||||||
|
|
||||||
|
$time_end = microtime(true);
|
||||||
|
$execution_time = round($time_end - $time_start, 2);
|
||||||
|
|
||||||
|
$this->addFlash('success', $this->trans('%1$s - webp images has been erased successfully in %2$ss', 'Modules.isthemecore.Admin', [$eraser->getFilesCount(), $execution_time]));
|
||||||
|
|
||||||
|
return $this->redirectToRoute('is_themecore_module_settings');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process form.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param FormHandlerInterface $formHandler
|
||||||
|
* @param string $hookName
|
||||||
|
*
|
||||||
|
* @return RedirectResponse
|
||||||
|
*/
|
||||||
|
private function processForm(Request $request, FormHandlerInterface $formHandler)
|
||||||
|
{
|
||||||
|
$form = $formHandler->getForm();
|
||||||
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
if ($form->isSubmitted()) {
|
||||||
|
if ($form->isValid()) {
|
||||||
|
$data = $form->getData();
|
||||||
|
$saveErrors = $formHandler->save($data);
|
||||||
|
|
||||||
|
if (!empty($data['webp_enabled'])) {
|
||||||
|
$generator = $this->get('oksydan.module.is_themecore.core.htaccess.htaccess_generator');
|
||||||
|
|
||||||
|
$generator->generate((bool) $data['webp_enabled']);
|
||||||
|
$generator->writeFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 === count($saveErrors)) {
|
||||||
|
$this->addFlash('success', $this->trans('Successful update.', 'Admin.Notifications.Success'));
|
||||||
|
} else {
|
||||||
|
$this->flashErrors($saveErrors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$formErrors = [];
|
||||||
|
foreach ($form->getErrors(true) as $error) {
|
||||||
|
$formErrors[] = $error->getMessage();
|
||||||
|
}
|
||||||
|
$this->flashErrors($formErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->redirectToRoute('is_themecore_module_settings');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return FormHandlerInterface
|
||||||
|
*/
|
||||||
|
private function getGeneralFormHandler()
|
||||||
|
{
|
||||||
|
/** @var FormHandlerInterface */
|
||||||
|
$formDataHandler = $this->get('oksydan.module.is_themecore.form.settings.general_form_data_handler');
|
||||||
|
|
||||||
|
return $formDataHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return FormHandlerInterface
|
||||||
|
*/
|
||||||
|
private function getWebpFormHandler()
|
||||||
|
{
|
||||||
|
/** @var FormHandlerInterface */
|
||||||
|
$formDataHandler = $this->get('oksydan.module.is_themecore.form.settings.webp_form_data_handler');
|
||||||
|
|
||||||
|
return $formDataHandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
is_themecore/src/Controller/Admin/index.php
Normal file
11
is_themecore/src/Controller/Admin/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
11
is_themecore/src/Controller/Front/index.php
Normal file
11
is_themecore/src/Controller/Front/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
11
is_themecore/src/Controller/index.php
Normal file
11
is_themecore/src/Controller/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
80
is_themecore/src/Core/Breadcrumbs/ThemeBreadcrumbs.php
Normal file
80
is_themecore/src/Core/Breadcrumbs/ThemeBreadcrumbs.php
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\Breadcrumbs;
|
||||||
|
|
||||||
|
class ThemeBreadcrumbs
|
||||||
|
{
|
||||||
|
protected $breadcrumbs = [];
|
||||||
|
public $pageName;
|
||||||
|
public $translator;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->context = \Context::getContext();
|
||||||
|
$this->pageName = $this->context->controller->getPageName();
|
||||||
|
$this->getAvailableBreadcrumbs();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getAvailableBreadcrumbs()
|
||||||
|
{
|
||||||
|
$this->breadcrumbs = $this->getCommonBreadcrumbs();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBreadcrumb()
|
||||||
|
{
|
||||||
|
$breadcrumb = [];
|
||||||
|
$breadcrumb['links'] = $this->getBreadcrumbByPageName();
|
||||||
|
$breadcrumb['count'] = count($breadcrumb['links']);
|
||||||
|
|
||||||
|
return $breadcrumb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBreadcrumbByPageName()
|
||||||
|
{
|
||||||
|
$breadcrumb = [];
|
||||||
|
|
||||||
|
if (isset($this->breadcrumbs[$this->pageName])) {
|
||||||
|
$breadcrumb = $this->breadcrumbs[$this->pageName];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $breadcrumb;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getCommonBreadcrumbs()
|
||||||
|
{
|
||||||
|
$pages = [
|
||||||
|
[
|
||||||
|
'controller' => 'cart',
|
||||||
|
'name' => $this->context->getTranslator()->trans('Shopping Cart', [], 'Shop.Theme.Checkout'),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'controller' => 'pagenotfound',
|
||||||
|
'name' => $this->context->getTranslator()->trans('404', [], 'Shop.Theme.Global'),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'controller' => 'stores',
|
||||||
|
'name' => $this->context->getTranslator()->trans('Our stores', [], 'Shop.Theme.Global'),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'controller' => 'sitemap',
|
||||||
|
'name' => $this->context->getTranslator()->trans('Sitemap', [], 'Shop.Theme.Global'),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$breadcrumbs = [];
|
||||||
|
foreach ($pages as $page) {
|
||||||
|
$breadcrumbs[$page['controller']] = [
|
||||||
|
[
|
||||||
|
'url' => $this->context->link->getPageLink('index'),
|
||||||
|
'title' => $this->context->getTranslator()->trans('Home', [], 'Shop.Theme.Global'),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'url' => $this->context->link->getPageLink($page['controller']),
|
||||||
|
'title' => $page['name'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $breadcrumbs;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
is_themecore/src/Core/Breadcrumbs/index.php
Normal file
11
is_themecore/src/Core/Breadcrumbs/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
233
is_themecore/src/Core/Htaccess/HtaccessGenerator.php
Normal file
233
is_themecore/src/Core/Htaccess/HtaccessGenerator.php
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\Htaccess;
|
||||||
|
|
||||||
|
class HtaccessGenerator
|
||||||
|
{
|
||||||
|
private $module;
|
||||||
|
private $domains = [];
|
||||||
|
private $medias = [];
|
||||||
|
|
||||||
|
protected $moduleWebpGeneratorFile;
|
||||||
|
protected $mediaDomains = null;
|
||||||
|
protected $tempContent = '';
|
||||||
|
protected $contentBefore = '';
|
||||||
|
protected $contentAfter = '';
|
||||||
|
|
||||||
|
protected $wrapperBlockComments = [
|
||||||
|
'COMMENT_START' => '~~start-is_themecore~~',
|
||||||
|
'COMMENT_END' => '~~end-is_themecore~~',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function __construct(\Is_themecore $module)
|
||||||
|
{
|
||||||
|
$this->domains = \Tools::getDomains();
|
||||||
|
$this->module = $module;
|
||||||
|
$this->moduleWebpGeneratorFile = "%{ENV:REWRITEBASE}modules/{$this->module->name}/webp.php";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generate($addRewrite = true): void
|
||||||
|
{
|
||||||
|
$htaccessFile = $this->getHtaccessFilePath();
|
||||||
|
|
||||||
|
if (file_exists($htaccessFile)) {
|
||||||
|
$content = \Tools::file_get_contents($htaccessFile);
|
||||||
|
|
||||||
|
if (preg_match('#^(.*)\# ' . $this->wrapperBlockComments['COMMENT_START'] . '.*\# ' . $this->wrapperBlockComments['COMMENT_END'] . '[^\n]*(.*)$#s', $content, $match)) {
|
||||||
|
$this->contentBefore = $match[1];
|
||||||
|
$this->contentAfter = $match[2];
|
||||||
|
} else {
|
||||||
|
$this->contentAfter = $content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($addRewrite) {
|
||||||
|
$this->generateHtaccessHeader();
|
||||||
|
|
||||||
|
$this->write('<IfModule mod_rewrite.c>');
|
||||||
|
$this->write('RewriteEngine On');
|
||||||
|
$this->writeNl();
|
||||||
|
$this->generateImagesRewrites();
|
||||||
|
$this->write('</IfModule>');
|
||||||
|
|
||||||
|
$this->write("# {$this->wrapperBlockComments['COMMENT_END']} Do not remove this comment");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->write($this->contentAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function writeFile(): bool
|
||||||
|
{
|
||||||
|
$htaccessFile = $this->getHtaccessFilePath();
|
||||||
|
|
||||||
|
if (!$writeToFile = @fopen($htaccessFile, 'wb')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fwrite($writeToFile, $this->tempContent)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($writeToFile);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getMediaDomains(): string
|
||||||
|
{
|
||||||
|
if ($this->mediaDomains === null) {
|
||||||
|
if (\Configuration::getMultiShopValues('PS_MEDIA_SERVER_1')
|
||||||
|
&& \Configuration::getMultiShopValues('PS_MEDIA_SERVER_2')
|
||||||
|
&& \Configuration::getMultiShopValues('PS_MEDIA_SERVER_3')
|
||||||
|
) {
|
||||||
|
$this->medias = [
|
||||||
|
\Configuration::getMultiShopValues('PS_MEDIA_SERVER_1'),
|
||||||
|
\Configuration::getMultiShopValues('PS_MEDIA_SERVER_2'),
|
||||||
|
\Configuration::getMultiShopValues('PS_MEDIA_SERVER_3'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->mediaDomains = '';
|
||||||
|
|
||||||
|
foreach ($this->medias as $media) {
|
||||||
|
foreach ($media as $mediaUrl) {
|
||||||
|
if ($mediaUrl) {
|
||||||
|
$this->mediaDomains .= 'RewriteCond %{HTTP_HOST} ^' . $mediaUrl . '$ [OR]' . PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->mediaDomains;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getDomainRewriteCond($domain): string
|
||||||
|
{
|
||||||
|
return "RewriteCond %{HTTP_HOST} ^$domain$";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function generateImagesRewrites(): void
|
||||||
|
{
|
||||||
|
foreach ($this->domains as $domain => $uri) {
|
||||||
|
$this->generateProductImagesRewrite($domain);
|
||||||
|
$this->generateCategoryImagesRewrite($domain);
|
||||||
|
$this->generateOtherImagesRewrite($domain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function writeMediaDomainsCondition()
|
||||||
|
{
|
||||||
|
$mediaDomains = $this->getMediaDomains();
|
||||||
|
|
||||||
|
if ($mediaDomains) {
|
||||||
|
$this->write($mediaDomains, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function generateProductImagesRewrite($domain): void
|
||||||
|
{
|
||||||
|
$domainRewriteCond = $this->getDomainRewriteCond($domain);
|
||||||
|
|
||||||
|
for ($i = 1; $i <= 7; ++$i) {
|
||||||
|
$imgPath = $imgName = '';
|
||||||
|
for ($j = 1; $j <= $i; ++$j) {
|
||||||
|
$imgPath .= '$' . $j . '/';
|
||||||
|
$imgName .= '$' . $j;
|
||||||
|
}
|
||||||
|
$imgName .= '$' . $j;
|
||||||
|
|
||||||
|
// WEBP FILE EXISTS
|
||||||
|
$this->writeMediaDomainsCondition();
|
||||||
|
$this->write($domainRewriteCond);
|
||||||
|
$this->write('RewriteCond %{DOCUMENT_ROOT}/img/p/' . $imgPath . $imgName . '$' . ($j + 1) . '.webp -f');
|
||||||
|
$this->write('RewriteRule ^' . str_repeat('([0-9])', $i) . '(\-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+\.webp$ %{ENV:REWRITEBASE}img/p/' . $imgPath . $imgName . '$' . ($j + 1) . '.webp [L]');
|
||||||
|
$this->writeNl();
|
||||||
|
|
||||||
|
// WEBP FILE NOT EXISTS
|
||||||
|
$this->writeMediaDomainsCondition();
|
||||||
|
$this->write($domainRewriteCond);
|
||||||
|
$this->write('RewriteCond %{DOCUMENT_ROOT}/img/p/' . $imgPath . $imgName . '$' . ($j + 1) . '.webp !-f');
|
||||||
|
$this->write('RewriteRule ^' . str_repeat('([0-9])', $i) . '(\-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+\.webp$ ' . $this->moduleWebpGeneratorFile . '?source=%{DOCUMENT_ROOT}/img/p/' . $imgPath . $imgName . '$' . ($j + 1) . '.webp [NC,L]');
|
||||||
|
$this->writeNl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function generateCategoryImagesRewrite($domain): void
|
||||||
|
{
|
||||||
|
$domainRewriteCond = $this->getDomainRewriteCond($domain);
|
||||||
|
|
||||||
|
// WEBP FILE EXISTS
|
||||||
|
$this->writeMediaDomainsCondition();
|
||||||
|
$this->write($domainRewriteCond);
|
||||||
|
$this->write('RewriteCond %{DOCUMENT_ROOT}/img/c/$1$2.webp -f');
|
||||||
|
$this->write('RewriteRule ^c/([0-9]+)(\-[\.*_a-zA-Z0-9-]*)(-[0-9]+)?/.+\.webp$ %{ENV:REWRITEBASE}img/c/$1$2.webp [L]');
|
||||||
|
$this->writeNl();
|
||||||
|
|
||||||
|
$this->writeMediaDomainsCondition();
|
||||||
|
$this->write($domainRewriteCond);
|
||||||
|
$this->write('RewriteCond %{DOCUMENT_ROOT}/img/c/$1$2$3.webp -f');
|
||||||
|
$this->write('RewriteRule ^c/([0-9]+)(\-[\.*_a-zA-Z0-9-]*)(-[0-9]+)?/.+\.webp$ %{ENV:REWRITEBASE}img/c/$1$2$3.webp [L]');
|
||||||
|
$this->writeNl();
|
||||||
|
|
||||||
|
// WEBP FILE NOT EXISTS
|
||||||
|
$this->writeMediaDomainsCondition();
|
||||||
|
$this->write($domainRewriteCond);
|
||||||
|
$this->write('RewriteCond %{DOCUMENT_ROOT}/img/c/$1$2.webp !-f');
|
||||||
|
$this->write('RewriteRule ^c/([0-9]+)(\-[\.*_a-zA-Z0-9-]*)(-[0-9]+)?/.+\.webp$ ' . $this->moduleWebpGeneratorFile . '?source=%{DOCUMENT_ROOT}/img/c/$1$2.webp [NC,L]');
|
||||||
|
$this->writeNl();
|
||||||
|
|
||||||
|
$this->writeMediaDomainsCondition();
|
||||||
|
$this->write($domainRewriteCond);
|
||||||
|
$this->write('RewriteCond %{DOCUMENT_ROOT}/img/c/$1$2$3.webp !-f');
|
||||||
|
$this->write('RewriteRule ^c/([0-9]+)(\-[\.*_a-zA-Z0-9-]*)(-[0-9]+)?/.+\.webp$ ' . $this->moduleWebpGeneratorFile . '?source=%{DOCUMENT_ROOT}/img/c/$1$2$3.webp [NC,L]');
|
||||||
|
$this->writeNl();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function generateOtherImagesRewrite($domain): void
|
||||||
|
{
|
||||||
|
$domainRewriteCond = $this->getDomainRewriteCond($domain);
|
||||||
|
|
||||||
|
// WEBP FILE NOT EXISTS
|
||||||
|
$this->writeMediaDomainsCondition();
|
||||||
|
$this->write($domainRewriteCond);
|
||||||
|
$this->write('RewriteCond %{REQUEST_FILENAME} !-f');
|
||||||
|
$this->write('RewriteRule ^(.*)\.webp$ ' . $this->moduleWebpGeneratorFile . '?source=%{DOCUMENT_ROOT}/$1.webp [NC,L]');
|
||||||
|
$this->writeNl();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function generateHtaccessHeader(): void
|
||||||
|
{
|
||||||
|
$this->write("# {$this->wrapperBlockComments['COMMENT_START']} Do not remove this comment");
|
||||||
|
$this->write('# Allow webp files to be sent by Apache 2.2');
|
||||||
|
$this->write('<IfModule !mod_authz_core.c>');
|
||||||
|
$this->write('<Files ~ "\\.(webp)$">', true, 1);
|
||||||
|
$this->write('Allow from all', true, 2);
|
||||||
|
$this->write('</Files>', true, 1);
|
||||||
|
$this->write('</IfModule>');
|
||||||
|
$this->writeNl();
|
||||||
|
|
||||||
|
$this->write('# Allow webp files to be sent by Apache 2.4');
|
||||||
|
$this->write('<IfModule mod_authz_core.c>');
|
||||||
|
$this->write('<Files ~ "\\.(webp)$">', true, 1);
|
||||||
|
$this->write('Require all granted', true, 2);
|
||||||
|
$this->write('allow from all', true, 2);
|
||||||
|
$this->write('</Files>', true, 1);
|
||||||
|
$this->write('</IfModule>');
|
||||||
|
$this->writeNl();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function write($line, $addEOL = true, $tabs = 0): void
|
||||||
|
{
|
||||||
|
$this->tempContent .= str_repeat("\t", $tabs) . $line . ($addEOL ? PHP_EOL : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function writeNl(): void
|
||||||
|
{
|
||||||
|
$this->write('');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getHtaccessFilePath(): string
|
||||||
|
{
|
||||||
|
return _PS_ROOT_DIR_ . '/.htaccess';
|
||||||
|
}
|
||||||
|
}
|
||||||
11
is_themecore/src/Core/Htaccess/index.php
Normal file
11
is_themecore/src/Core/Htaccess/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
60
is_themecore/src/Core/ListingDisplay/ThemeListDisplay.php
Normal file
60
is_themecore/src/Core/ListingDisplay/ThemeListDisplay.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\ListingDisplay;
|
||||||
|
|
||||||
|
use Oksydan\Module\IsThemeCore\Form\Settings\GeneralConfiguration;
|
||||||
|
use Symfony\Component\HttpFoundation\Cookie;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
class ThemeListDisplay
|
||||||
|
{
|
||||||
|
private $cookieName = 'listingDisplayType';
|
||||||
|
private $displayList = [
|
||||||
|
'grid',
|
||||||
|
'list',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected function getRequest(): Request
|
||||||
|
{
|
||||||
|
Request::setFactory(static function ($query, $request, $attributes, $cookies, $files, $server, $content) {
|
||||||
|
return new Request($query, $request, $attributes, $cookies, [], $server, $content);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Request::createFromGlobals();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDisplay($display): Response
|
||||||
|
{
|
||||||
|
if (!in_array($display, $this->displayList)) {
|
||||||
|
$display = \Configuration::get(GeneralConfiguration::THEMECORE_DISPLAY_LIST);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = new Response();
|
||||||
|
|
||||||
|
$response->headers->setCookie(new Cookie(
|
||||||
|
$this->cookieName,
|
||||||
|
$display,
|
||||||
|
(new \DateTime('now'))->modify('+ 30 days')->getTimestamp(),
|
||||||
|
'/'
|
||||||
|
));
|
||||||
|
|
||||||
|
return $response->sendHeaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplay()
|
||||||
|
{
|
||||||
|
$displayFromCookie = $this->getRequest()->cookies->get($this->cookieName);
|
||||||
|
|
||||||
|
if ($displayFromCookie) {
|
||||||
|
return $displayFromCookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
return \Configuration::get(GeneralConfiguration::THEMECORE_DISPLAY_LIST);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplayOptions()
|
||||||
|
{
|
||||||
|
return $this->displayList;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
is_themecore/src/Core/ListingDisplay/index.php
Normal file
11
is_themecore/src/Core/ListingDisplay/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
54
is_themecore/src/Core/Partytown/FilesInstallation.php
Normal file
54
is_themecore/src/Core/Partytown/FilesInstallation.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\Partytown;
|
||||||
|
|
||||||
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
|
|
||||||
|
class FilesInstallation
|
||||||
|
{
|
||||||
|
private \Is_themecore $module;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
\Is_themecore $module
|
||||||
|
) {
|
||||||
|
$this->module = $module;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function installFiles(): void
|
||||||
|
{
|
||||||
|
$this->installPartytown();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getFileSystem(): Filesystem
|
||||||
|
{
|
||||||
|
return new Filesystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function installPartytown(): void
|
||||||
|
{
|
||||||
|
$source = _PS_MODULE_DIR_ . $this->module->name . '/public/~partytown';
|
||||||
|
$destination = _PS_ROOT_DIR_ . '/~partytown';
|
||||||
|
$fileSystem = $this->getFileSystem();
|
||||||
|
|
||||||
|
if (!file_exists($source)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_exists($destination)) {
|
||||||
|
$fileSystem->remove($destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileSystem->mkdir($destination);
|
||||||
|
|
||||||
|
$directoryIterator = new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS);
|
||||||
|
$iterator = new \RecursiveIteratorIterator($directoryIterator, \RecursiveIteratorIterator::SELF_FIRST);
|
||||||
|
|
||||||
|
foreach ($iterator as $item) {
|
||||||
|
if ($item->isDir()) {
|
||||||
|
$fileSystem->mkdir($destination . DIRECTORY_SEPARATOR . $iterator->getSubPathName());
|
||||||
|
} else {
|
||||||
|
$fileSystem->copy($item, $destination . DIRECTORY_SEPARATOR . $iterator->getSubPathName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
is_themecore/src/Core/Partytown/PartytownScript.php
Normal file
36
is_themecore/src/Core/Partytown/PartytownScript.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\Partytown;
|
||||||
|
|
||||||
|
class PartytownScript
|
||||||
|
{
|
||||||
|
private \Is_themecore $module;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
\Is_themecore $module
|
||||||
|
) {
|
||||||
|
$this->module = $module;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getScriptPath(): string
|
||||||
|
{
|
||||||
|
return _PS_MODULE_DIR_ . $this->module->name . '/public/partytown.js';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getScriptUri(): string
|
||||||
|
{
|
||||||
|
return $this->module->getPathUri() . '/public/partytown.js';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getScriptContent(): string
|
||||||
|
{
|
||||||
|
$script = '';
|
||||||
|
$filePath = $this->getScriptPath();
|
||||||
|
|
||||||
|
if (file_exists($filePath)) {
|
||||||
|
$script = file_get_contents($filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $script;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\Partytown;
|
||||||
|
|
||||||
|
class PartytownScriptUriResolver
|
||||||
|
{
|
||||||
|
private \Context $context;
|
||||||
|
|
||||||
|
const PUBLIC_PARTYTOWN_PATH = '~partytown/';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
\Context $context
|
||||||
|
) {
|
||||||
|
$this->context = $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getScriptUri(): string
|
||||||
|
{
|
||||||
|
return $this->context->shop->physical_uri . self::PUBLIC_PARTYTOWN_PATH;
|
||||||
|
}
|
||||||
|
}
|
||||||
192
is_themecore/src/Core/Smarty/SmartyHelperFunctions.php
Normal file
192
is_themecore/src/Core/Smarty/SmartyHelperFunctions.php
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\Smarty;
|
||||||
|
|
||||||
|
use Oksydan\Module\IsThemeCore\Core\Webp\WebpPictureGenerator;
|
||||||
|
use Oksydan\Module\IsThemeCore\Form\Settings\WebpConfiguration;
|
||||||
|
|
||||||
|
class SmartyHelperFunctions
|
||||||
|
{
|
||||||
|
public static function generateImagesSources($params)
|
||||||
|
{
|
||||||
|
$image = $params['image'];
|
||||||
|
$size = $params['size'];
|
||||||
|
$lazyLoad = isset($params['lazyload']) ? $params['lazyload'] : true;
|
||||||
|
$attributes = [];
|
||||||
|
$highDpiImagesEnabled = (bool) \Configuration::get('PS_HIGHT_DPI');
|
||||||
|
|
||||||
|
$srcAttributePrefix = $lazyLoad ? 'data-' : '';
|
||||||
|
|
||||||
|
$img = $image['bySize'][$size]['url'];
|
||||||
|
|
||||||
|
if ($highDpiImagesEnabled) {
|
||||||
|
$size2x = $size . '2x';
|
||||||
|
$img2x = str_replace($size, $size2x, $img);
|
||||||
|
$attributeName = $srcAttributePrefix . 'srcset';
|
||||||
|
$attributes[$attributeName] = "$img, $img2x 2x";
|
||||||
|
} else {
|
||||||
|
$attributeName = $srcAttributePrefix . 'src';
|
||||||
|
$attributes[$attributeName] = $img;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($lazyLoad) {
|
||||||
|
$width = $image['bySize'][$size]['width'];
|
||||||
|
$height = $image['bySize'][$size]['height'];
|
||||||
|
$placeholderSrc = self::generateImageSvgPlaceholder(['width' => $width, 'height' => $height]);
|
||||||
|
|
||||||
|
$attributes['src'] = $placeholderSrc;
|
||||||
|
}
|
||||||
|
|
||||||
|
$attributesToPrint = [];
|
||||||
|
|
||||||
|
foreach ($attributes as $attr => $value) {
|
||||||
|
$attributesToPrint[] = $attr . '="' . $value . '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(PHP_EOL, $attributesToPrint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function generateImageSvgPlaceholder($params)
|
||||||
|
{
|
||||||
|
$width = $params['width'];
|
||||||
|
$height = $params['height'];
|
||||||
|
|
||||||
|
return "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='$width' height='$height' viewBox='0 0 1 1'%3E%3C/svg%3E";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function appendParamToUrl($params)
|
||||||
|
{
|
||||||
|
list(
|
||||||
|
'url' => $url,
|
||||||
|
'key' => $key,
|
||||||
|
'value' => $value
|
||||||
|
) = $params;
|
||||||
|
|
||||||
|
$replace = false;
|
||||||
|
|
||||||
|
if (isset($params['replace'])) {
|
||||||
|
$replace = $params['replace'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($value)) {
|
||||||
|
$value = [$value];
|
||||||
|
} else {
|
||||||
|
$replace = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($value as $qValue) {
|
||||||
|
$query = parse_url($url, PHP_URL_QUERY);
|
||||||
|
|
||||||
|
if ($query) {
|
||||||
|
if ($replace) {
|
||||||
|
parse_str($query, $queryParams);
|
||||||
|
$queryParams[$key] = $qValue;
|
||||||
|
$url = str_replace("?$query", '?' . http_build_query($queryParams), $url);
|
||||||
|
} else {
|
||||||
|
$queryParams = [];
|
||||||
|
$queryParams[$key] = $qValue;
|
||||||
|
$url .= '&' . http_build_query($queryParams);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$url .= '?' . urlencode($key) . '=' . urlencode($qValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function imagesBlock($params, $content, $smarty)
|
||||||
|
{
|
||||||
|
$webpEnabled = isset($params['webpEnabled']) ? $params['webpEnabled'] : \Configuration::get(WebpConfiguration::THEMECORE_WEBP_ENABLED);
|
||||||
|
|
||||||
|
if ($webpEnabled && !empty($content)) {
|
||||||
|
$pictureGenerator = new WebpPictureGenerator($content);
|
||||||
|
|
||||||
|
$pictureGenerator
|
||||||
|
->loadContent()
|
||||||
|
->generatePictureTags();
|
||||||
|
|
||||||
|
return $pictureGenerator->getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function displayMobileBlock($params, $content, $smarty)
|
||||||
|
{
|
||||||
|
if (!empty($content) && \Context::getContext()->isMobile()) {
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function displayDesktopBlock($params, $content, $smarty)
|
||||||
|
{
|
||||||
|
if (!empty($content) && !\Context::getContext()->isMobile()) {
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function cmsImagesBlock($params, $content, $smarty)
|
||||||
|
{
|
||||||
|
$doc = new \DOMDocument();
|
||||||
|
$doc->loadHTML('<meta http-equiv="Content-Type" content="charset=utf-8">' . $content);
|
||||||
|
$context = \Context::getContext();
|
||||||
|
|
||||||
|
$images = $doc->getElementsByTagName('img');
|
||||||
|
|
||||||
|
$domains = \Tools::getDomains();
|
||||||
|
$medias = [
|
||||||
|
\Configuration::get('PS_MEDIA_SERVER_1'),
|
||||||
|
\Configuration::get('PS_MEDIA_SERVER_2'),
|
||||||
|
\Configuration::get('PS_MEDIA_SERVER_3'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$internalUrls = [];
|
||||||
|
|
||||||
|
foreach ($domains as $domain => $options) {
|
||||||
|
$internalUrls[] = $domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($medias as $media) {
|
||||||
|
if ($media) {
|
||||||
|
$internalUrls[] = $media;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($images as $image) {
|
||||||
|
$newImg = $doc->createElement('img');
|
||||||
|
$src = urldecode($image->attributes->getNamedItem('src')->nodeValue);
|
||||||
|
|
||||||
|
if (!preg_match('/' . implode('|', $internalUrls) . '/i', $src)) {
|
||||||
|
$newImg->setAttribute('data-external-url', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($image->attributes as $attribute) {
|
||||||
|
$newImg->setAttribute($attribute->nodeName, $attribute->nodeValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
$image->parentNode->replaceChild($newImg, $image);
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = $doc->saveHTML();
|
||||||
|
$content = str_replace('<meta http-equiv="Content-Type" content="charset=utf-8">', '', $content);
|
||||||
|
|
||||||
|
$webpEnabled = isset($params['webpEnabled']) ? $params['webpEnabled'] : \Configuration::get(WebpConfiguration::THEMECORE_WEBP_ENABLED);
|
||||||
|
|
||||||
|
if ($webpEnabled && !empty($content)) {
|
||||||
|
$pictureGenerator = new WebpPictureGenerator($content);
|
||||||
|
|
||||||
|
$pictureGenerator
|
||||||
|
->loadContent()
|
||||||
|
->generatePictureTags();
|
||||||
|
|
||||||
|
return $pictureGenerator->getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\StructuredData;
|
||||||
|
|
||||||
|
use Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataPresenterInterface;
|
||||||
|
use Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataProviderInterface;
|
||||||
|
|
||||||
|
abstract class AbstractStructuredData
|
||||||
|
{
|
||||||
|
private StructuredDataProviderInterface $provider;
|
||||||
|
private StructuredDataPresenterInterface $presenter;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
StructuredDataProviderInterface $provider,
|
||||||
|
StructuredDataPresenterInterface $presenter
|
||||||
|
) {
|
||||||
|
$this->provider = $provider;
|
||||||
|
$this->presenter = $presenter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return formatted json data
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getFormattedData(): string
|
||||||
|
{
|
||||||
|
$data = $this->provider->getData();
|
||||||
|
|
||||||
|
$jsonData = $this->presenter->present($data);
|
||||||
|
|
||||||
|
\Hook::exec('actionStructuredData' . ucfirst($this->getStructuredDataType()),
|
||||||
|
[
|
||||||
|
'jsonData' => &$jsonData,
|
||||||
|
'rawData' => $data,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (empty($jsonData)) {
|
||||||
|
return '';
|
||||||
|
} else {
|
||||||
|
return json_encode($jsonData, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\StructuredData;
|
||||||
|
|
||||||
|
class BreadcrumbStructuredData extends AbstractStructuredData implements StructuredDataInterface
|
||||||
|
{
|
||||||
|
public function getStructuredDataType(): string
|
||||||
|
{
|
||||||
|
return 'breadcrumb';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter;
|
||||||
|
|
||||||
|
class StructuredDataBreadcrumbPresenter implements StructuredDataPresenterInterface
|
||||||
|
{
|
||||||
|
private array $presentedData = [];
|
||||||
|
private array $breadcrumbData = [];
|
||||||
|
|
||||||
|
public function present($data): array
|
||||||
|
{
|
||||||
|
$this->breadcrumbData = $data;
|
||||||
|
|
||||||
|
$this->presentBreadcrumbData();
|
||||||
|
|
||||||
|
return $this->presentedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function presentBreadcrumbData(): void
|
||||||
|
{
|
||||||
|
$breadcrumbs = $this->breadcrumbData['links'];
|
||||||
|
|
||||||
|
if ($this->breadcrumbData['count'] > 1) {
|
||||||
|
$this->presentedData['@context'] = 'http://schema.org';
|
||||||
|
$this->presentedData['@type'] = 'BreadcrumbList';
|
||||||
|
$this->presentedData['itemListElement'] = [];
|
||||||
|
|
||||||
|
foreach ($breadcrumbs as $i => $breadcrumb) {
|
||||||
|
$this->presentedData['itemListElement'][] = [
|
||||||
|
'@type' => 'ListItem',
|
||||||
|
'position' => $i + 1,
|
||||||
|
'name' => $breadcrumb['title'],
|
||||||
|
'item' => $breadcrumb['url'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter;
|
||||||
|
|
||||||
|
interface StructuredDataPresenterInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Return formatted data
|
||||||
|
*
|
||||||
|
* @param array $data Data from provider
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function present($data);
|
||||||
|
}
|
||||||
@ -0,0 +1,166 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter;
|
||||||
|
|
||||||
|
class StructuredDataProductPresenter implements StructuredDataPresenterInterface
|
||||||
|
{
|
||||||
|
private $presentedData = [];
|
||||||
|
private $productData;
|
||||||
|
private $context;
|
||||||
|
|
||||||
|
public function __construct(\Context $context)
|
||||||
|
{
|
||||||
|
$this->context = $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function present($data): array
|
||||||
|
{
|
||||||
|
$this->productData = $data;
|
||||||
|
|
||||||
|
$this->getProductBasics();
|
||||||
|
$this->getProductIdentifier();
|
||||||
|
$this->getProductBrandData();
|
||||||
|
$this->getProductReviewsData();
|
||||||
|
$this->getProductOffers();
|
||||||
|
|
||||||
|
return $this->presentedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getProductBasics(): void
|
||||||
|
{
|
||||||
|
$this->presentedData['@context'] = 'http://schema.org/';
|
||||||
|
$this->presentedData['@type'] = 'Product';
|
||||||
|
$this->presentedData['name'] = $this->productData['name'];
|
||||||
|
$this->presentedData['category'] = $this->productData['category_name'];
|
||||||
|
|
||||||
|
if (!empty($this->productData['description_short'])) {
|
||||||
|
$this->presentedData['description'] = strip_tags($this->productData['description_short']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->productData['default_image']) {
|
||||||
|
$this->presentedData['image'] = $this->productData['default_image']['large']['url'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->productData['reference']) {
|
||||||
|
$this->presentedData['sku'] = $this->productData['reference'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->productData['weight']) && $this->productData['weight'] > 0) {
|
||||||
|
$this->presentedData['weight'] = [
|
||||||
|
'@context' => 'https://schema.org',
|
||||||
|
'@type' => 'QuantitativeValue',
|
||||||
|
'value' => $this->productData['weight'],
|
||||||
|
'unitCode' => $this->productData['weight_unit'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getProductBrandData(): void
|
||||||
|
{
|
||||||
|
if (empty($this->productData['id_manufacturer'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$productManufacturer = new \Manufacturer((int) $this->productData['id_manufacturer'], $this->context->language->id);
|
||||||
|
|
||||||
|
if (!empty($productManufacturer->name)) {
|
||||||
|
$this->presentedData['brand'] = [
|
||||||
|
'@type' => 'Brand',
|
||||||
|
'name' => $productManufacturer->name,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getProductIdentifier(): void
|
||||||
|
{
|
||||||
|
if (!empty($this->productData['ean13'])) {
|
||||||
|
$this->presentedData['gtin13'] = $this->productData['ean13'];
|
||||||
|
} elseif (!empty($this->productData['upc'])) {
|
||||||
|
$this->presentedData['gtin13'] = '0' . $this->productData['upc'];
|
||||||
|
} elseif (!empty($this->productData['isbn'])) {
|
||||||
|
$this->presentedData['isbn'] = $this->productData['isbn'];
|
||||||
|
} elseif (!empty($this->productData['reference'])) {
|
||||||
|
$this->presentedData['mpn'] = $this->productData['reference'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getProductOffers(): void
|
||||||
|
{
|
||||||
|
if (!$this->productData['show_price']) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->presentedData['offers'] = [
|
||||||
|
'@type' => 'Offer',
|
||||||
|
'name' => $this->productData['name'],
|
||||||
|
'price' => $this->productData['price_amount'],
|
||||||
|
'url' => $this->productData['url'],
|
||||||
|
'priceCurrency' => $this->context->currency->iso_code,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (count($this->productData['images']) > 0) {
|
||||||
|
$images = [];
|
||||||
|
|
||||||
|
foreach ($this->productData['images'] as $img) {
|
||||||
|
$images[] = $img['large']['url'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->presentedData['offers']['image'] = $images;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->productData['reference']) {
|
||||||
|
$this->presentedData['offers']['sku'] = $this->productData['reference'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->presentedData['offers']['availability'] = $this->productData['quantity'] > 0 || $this->productData['allow_oosp'] ? 'http://schema.org/InStock' : 'http://schema.org/OutOfStock';
|
||||||
|
|
||||||
|
if ($this->productData['show_condition'] && isset($this->productData['condition'])) {
|
||||||
|
$this->presentedData['offers']['itemCondition'] = $this->productData['condition']['schema_url'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->productData['specific_prices'] && $this->productData['specific_prices']['to'] > (new \DateTime())->format('Y-m-d H:i:s')) {
|
||||||
|
$date = new \DateTime($this->productData['specific_prices']['to']);
|
||||||
|
$this->presentedData['offers']['priceValidUntil'] = $date->format('Y-m-d');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getProductReviewsData(): void
|
||||||
|
{
|
||||||
|
if (empty($this->productData['productRating'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$reviews = [];
|
||||||
|
|
||||||
|
foreach ($this->productData['productRating']['reviews'] as $review) {
|
||||||
|
$datePublished = new \DateTime($review['date_add']);
|
||||||
|
|
||||||
|
$reviews[] = [
|
||||||
|
'@type' => 'Review',
|
||||||
|
'author' => [
|
||||||
|
'@type' => 'Person',
|
||||||
|
'name' => $review['customer_name'],
|
||||||
|
],
|
||||||
|
'name' => $review['title'],
|
||||||
|
'reviewBody' => $review['content'],
|
||||||
|
'datePublished' => $datePublished->format(\DateTime::ATOM),
|
||||||
|
'reviewRating' => [
|
||||||
|
'@type' => 'Rating',
|
||||||
|
'ratingValue' => $review['grade'],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$aggregateRating = [
|
||||||
|
'@type' => 'AggregateRating',
|
||||||
|
'ratingValue' => $this->productData['productRating']['averageGrade'],
|
||||||
|
'ratingCount' => $this->productData['productRating']['commentsNb'],
|
||||||
|
'reviewCount' => $this->productData['productRating']['commentsNb'],
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($reviews) {
|
||||||
|
$this->presentedData['review'] = $reviews;
|
||||||
|
}
|
||||||
|
$this->presentedData['aggregateRating'] = $aggregateRating;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter;
|
||||||
|
|
||||||
|
class StructuredDataShopPresenter implements StructuredDataPresenterInterface
|
||||||
|
{
|
||||||
|
private $presentedData = [];
|
||||||
|
private $shopData;
|
||||||
|
private $context;
|
||||||
|
|
||||||
|
public function __construct(\Context $context)
|
||||||
|
{
|
||||||
|
$this->context = $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function present($data): array
|
||||||
|
{
|
||||||
|
$this->shopData = $data;
|
||||||
|
|
||||||
|
$this->presentShopData();
|
||||||
|
|
||||||
|
return $this->presentedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function presentShopData(): void
|
||||||
|
{
|
||||||
|
$this->presentedData['@context'] = 'http://schema.org';
|
||||||
|
$this->presentedData['@type'] = 'Organization';
|
||||||
|
$this->presentedData['name'] = $this->shopData['name'];
|
||||||
|
$this->presentedData['url'] = $this->context->link->getPageLink('index');
|
||||||
|
$this->presentedData['logo'] = [
|
||||||
|
'@type' => 'ImageObject',
|
||||||
|
'url' => $this->shopData['logo'],
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($this->shopData['phone']) {
|
||||||
|
$this->presentedData['contactPoint'] = [
|
||||||
|
'@type' => 'ContactPoint',
|
||||||
|
'telephone' => $this->shopData['phone'],
|
||||||
|
'contactType' => 'customer service',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$address = $this->shopData['address'];
|
||||||
|
$postalCode = $address['postcode'];
|
||||||
|
$city = $address['city'];
|
||||||
|
$country = $address['country'];
|
||||||
|
$addressRegion = $address['state'];
|
||||||
|
$streetAddress = $address['address1'];
|
||||||
|
|
||||||
|
if ($postalCode || $city || $country || $addressRegion || $streetAddress) {
|
||||||
|
$this->presentedData['address'] = [
|
||||||
|
'@type' => 'PostalAddress',
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($postalCode) {
|
||||||
|
$this->presentedData['address']['postalCode'] = $postalCode;
|
||||||
|
}
|
||||||
|
if ($streetAddress) {
|
||||||
|
$this->presentedData['address']['streetAddress'] = $streetAddress;
|
||||||
|
}
|
||||||
|
if ($country || $city) {
|
||||||
|
$addressLocality = '';
|
||||||
|
if ($city) {
|
||||||
|
$addressLocality = $city;
|
||||||
|
}
|
||||||
|
if ($country) {
|
||||||
|
$addressLocality .= ($addressLocality != '' ? ', ' : '') . $country;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->presentedData['address']['addressLocality'] = $addressLocality;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter;
|
||||||
|
|
||||||
|
class StructuredDataWebsitePresenter implements StructuredDataPresenterInterface
|
||||||
|
{
|
||||||
|
private $presentedData = [];
|
||||||
|
private $websiteData;
|
||||||
|
private $context;
|
||||||
|
|
||||||
|
public function __construct(\Context $context)
|
||||||
|
{
|
||||||
|
$this->context = $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function present($data): array
|
||||||
|
{
|
||||||
|
$this->websiteData = $data;
|
||||||
|
|
||||||
|
$this->presentShopData();
|
||||||
|
|
||||||
|
return $this->presentedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function presentShopData(): void
|
||||||
|
{
|
||||||
|
$this->presentedData['@context'] = 'http://schema.org';
|
||||||
|
$this->presentedData['@type'] = 'WebSite';
|
||||||
|
$this->presentedData['url'] = $this->context->link->getPageLink('index');
|
||||||
|
$this->presentedData['image'] = [
|
||||||
|
'@type' => 'ImageObject',
|
||||||
|
'url' => $this->websiteData['logo'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\StructuredData;
|
||||||
|
|
||||||
|
class ProductStructuredData extends AbstractStructuredData implements StructuredDataInterface
|
||||||
|
{
|
||||||
|
public function getStructuredDataType(): string
|
||||||
|
{
|
||||||
|
return 'product';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Provider;
|
||||||
|
|
||||||
|
class StructuredDataBreadcrumbProvider implements StructuredDataProviderInterface
|
||||||
|
{
|
||||||
|
protected \Context $context;
|
||||||
|
|
||||||
|
public function __construct(\Context $context)
|
||||||
|
{
|
||||||
|
$this->context = $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getData(): array
|
||||||
|
{
|
||||||
|
return $this->context->controller->getBreadcrumb();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Provider;
|
||||||
|
|
||||||
|
class StructuredDataProductProvider implements StructuredDataProviderInterface
|
||||||
|
{
|
||||||
|
private array $data = [];
|
||||||
|
private \Context $context;
|
||||||
|
|
||||||
|
public function __construct(\Context $context)
|
||||||
|
{
|
||||||
|
$this->context = $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function provideProductCommentsDataIfModuleEnabled(): void
|
||||||
|
{
|
||||||
|
$commentsData = [];
|
||||||
|
|
||||||
|
if (\Module::isEnabled('productcomments')) {
|
||||||
|
$productCommentRepository = $this->context->controller->getContainer()->get('product_comment_repository');
|
||||||
|
$commentsModerate = (bool) \Configuration::get('PRODUCT_COMMENTS_MODERATE');
|
||||||
|
$commentsNb = $productCommentRepository->getCommentsNumber($this->data['id'], $commentsModerate);
|
||||||
|
|
||||||
|
if ($commentsNb > 0) {
|
||||||
|
$averageGrade = $productCommentRepository->getAverageGrade($this->data['id'], $commentsModerate);
|
||||||
|
$reviewsData = $productCommentRepository->paginate($this->data['id'], 1, 50, $commentsModerate); // get 50 reviews
|
||||||
|
|
||||||
|
$commentsData = [
|
||||||
|
'averageGrade' => $averageGrade,
|
||||||
|
'commentsNb' => $commentsNb,
|
||||||
|
'reviews' => $reviewsData,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->data['productRating'] = $commentsData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProductData(): void
|
||||||
|
{
|
||||||
|
$this->data = $this->context->controller->getTemplateVarProduct()->jsonSerialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getData(): array
|
||||||
|
{
|
||||||
|
$this->getProductData();
|
||||||
|
$this->provideProductCommentsDataIfModuleEnabled();
|
||||||
|
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Provider;
|
||||||
|
|
||||||
|
interface StructuredDataProviderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Provide data
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getData();
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Provider;
|
||||||
|
|
||||||
|
class StructuredDataShopProvider implements StructuredDataProviderInterface
|
||||||
|
{
|
||||||
|
protected \Context $context;
|
||||||
|
|
||||||
|
public function __construct(\Context $context)
|
||||||
|
{
|
||||||
|
$this->context = $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getData(): array
|
||||||
|
{
|
||||||
|
return $this->context->smarty->getTemplateVars('shop');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Provider;
|
||||||
|
|
||||||
|
class StructuredDataWebsiteProvider implements StructuredDataProviderInterface
|
||||||
|
{
|
||||||
|
protected \Context $context;
|
||||||
|
|
||||||
|
public function __construct(\Context $context)
|
||||||
|
{
|
||||||
|
$this->context = $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getData(): array
|
||||||
|
{
|
||||||
|
return $this->context->smarty->getTemplateVars('shop');
|
||||||
|
}
|
||||||
|
}
|
||||||
11
is_themecore/src/Core/StructuredData/ShopStructuredData.php
Normal file
11
is_themecore/src/Core/StructuredData/ShopStructuredData.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\StructuredData;
|
||||||
|
|
||||||
|
class ShopStructuredData extends AbstractStructuredData implements StructuredDataInterface
|
||||||
|
{
|
||||||
|
public function getStructuredDataType(): string
|
||||||
|
{
|
||||||
|
return 'shop';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\StructuredData;
|
||||||
|
|
||||||
|
interface StructuredDataInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Return formatted json data
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getFormattedData(): string;
|
||||||
|
|
||||||
|
public function getStructuredDataType(): string;
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\StructuredData;
|
||||||
|
|
||||||
|
class WebsiteStructuredData extends AbstractStructuredData implements StructuredDataInterface
|
||||||
|
{
|
||||||
|
public function getStructuredDataType(): string
|
||||||
|
{
|
||||||
|
return 'website';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\ThemeAssets;
|
||||||
|
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
|
class ThemeAssetConfigProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $fileContentRead = false;
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $fileParsed = [];
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $themeAssetsFileDir;
|
||||||
|
|
||||||
|
public function __construct($themeDir)
|
||||||
|
{
|
||||||
|
$this->themeAssetsFileDir = $themeDir . 'config/assets.yml';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFileParsed(): array
|
||||||
|
{
|
||||||
|
if (!$this->fileContentRead) {
|
||||||
|
if (file_exists($this->themeAssetsFileDir)) {
|
||||||
|
$this->fileParsed = Yaml::parse(file_get_contents($this->themeAssetsFileDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->fileContentRead = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->fileParsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCssAssets(): array
|
||||||
|
{
|
||||||
|
$cssAssets = [];
|
||||||
|
|
||||||
|
if (!empty($this->getFileParsed()['css'])) {
|
||||||
|
$cssAssets = $this->getFileParsed()['css'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cssAssets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getJsAssets(): array
|
||||||
|
{
|
||||||
|
$jsAssets = [];
|
||||||
|
|
||||||
|
if (!empty($this->getFileParsed()['js'])) {
|
||||||
|
$jsAssets = $this->getFileParsed()['js'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $jsAssets;
|
||||||
|
}
|
||||||
|
}
|
||||||
140
is_themecore/src/Core/ThemeAssets/ThemeAssetsRegister.php
Normal file
140
is_themecore/src/Core/ThemeAssets/ThemeAssetsRegister.php
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\ThemeAssets;
|
||||||
|
|
||||||
|
class ThemeAssetsRegister
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ThemeAssetConfigProvider
|
||||||
|
*/
|
||||||
|
private $assetsDataProvider;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $currentPageName;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $themeName;
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $cssAssets = [];
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $jsAssets = [];
|
||||||
|
|
||||||
|
public function __construct(ThemeAssetConfigProvider $assetsDataProvider, \Context $context)
|
||||||
|
{
|
||||||
|
$this->assetsDataProvider = $assetsDataProvider;
|
||||||
|
$this->context = $context;
|
||||||
|
$this->themeName = $this->context->shop->theme->getName();
|
||||||
|
$this->currentPageName = $this->context->controller->getPageName();
|
||||||
|
$this->themePath = 'themes/' . $this->themeName . '/assets/';
|
||||||
|
$this->cssAssets = $assetsDataProvider->getCssAssets();
|
||||||
|
$this->jsAssets = $assetsDataProvider->getJsAssets();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFilteredCssAssetsByPage(): array
|
||||||
|
{
|
||||||
|
return $this->filterAssetsArrayByPage($this->cssAssets);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFilteredJsAssetsByPage(): array
|
||||||
|
{
|
||||||
|
return $this->filterAssetsArrayByPage($this->jsAssets);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function filterAssetsArrayByPage($assetsArray): array
|
||||||
|
{
|
||||||
|
$pageName = $this->currentPageName;
|
||||||
|
|
||||||
|
return array_filter($assetsArray, function ($asset) use ($pageName) {
|
||||||
|
if (empty($asset['include'])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($pageName, $asset['include'])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($asset['include'] as $matchType) {
|
||||||
|
$regex = str_replace(
|
||||||
|
['\*'],
|
||||||
|
['.*', '.'],
|
||||||
|
preg_quote($matchType)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (preg_match('/^' . $regex . '$/is', $pageName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerThemeAssets(): void
|
||||||
|
{
|
||||||
|
$this->registerJsAssets();
|
||||||
|
$this->registerCssAssets();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerJsAssets(): void
|
||||||
|
{
|
||||||
|
$assetsToRegister = $this->getFilteredJsAssetsByPage();
|
||||||
|
|
||||||
|
$default_params = [
|
||||||
|
'position' => \AbstractAssetManager::DEFAULT_JS_POSITION,
|
||||||
|
'priority' => \AbstractAssetManager::DEFAULT_PRIORITY,
|
||||||
|
'inline' => false,
|
||||||
|
'attributes' => null,
|
||||||
|
'server' => 'local',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($assetsToRegister as $id => $asset) {
|
||||||
|
$params = array_merge($default_params, $asset);
|
||||||
|
$file = $params['server'] === 'local' ? $this->themePath . 'js/' . $asset['fileName'] : $asset['fileName'];
|
||||||
|
|
||||||
|
$this->context->controller->registerJavascript(
|
||||||
|
'theme-' . $id,
|
||||||
|
$file,
|
||||||
|
[
|
||||||
|
'position' => $params['position'],
|
||||||
|
'priority' => $params['priority'],
|
||||||
|
'inline' => $params['inline'],
|
||||||
|
'attributes' => $params['attributes'],
|
||||||
|
'server' => $params['server'],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerCssAssets(): void
|
||||||
|
{
|
||||||
|
$assetsToRegister = $this->getFilteredCssAssetsByPage();
|
||||||
|
|
||||||
|
$default_params = [
|
||||||
|
'media' => \AbstractAssetManager::DEFAULT_MEDIA,
|
||||||
|
'priority' => \AbstractAssetManager::DEFAULT_PRIORITY,
|
||||||
|
'inline' => false,
|
||||||
|
'server' => 'local',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($assetsToRegister as $id => $asset) {
|
||||||
|
$params = array_merge($default_params, $asset);
|
||||||
|
$file = $params['server'] === 'local' ? $this->themePath . 'css/' . $asset['fileName'] : $asset['fileName'];
|
||||||
|
|
||||||
|
$this->context->controller->registerStylesheet(
|
||||||
|
'theme-' . $id,
|
||||||
|
$file,
|
||||||
|
[
|
||||||
|
'media' => $params['media'],
|
||||||
|
'priority' => $params['priority'],
|
||||||
|
'server' => $params['server'],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
is_themecore/src/Core/Webp/RelatedImageFileFinder.php
Normal file
38
is_themecore/src/Core/Webp/RelatedImageFileFinder.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\Webp;
|
||||||
|
|
||||||
|
class RelatedImageFileFinder
|
||||||
|
{
|
||||||
|
protected $allowedImagesExtensions = ['jpg', 'png', 'jpeg'];
|
||||||
|
|
||||||
|
public function setAllowedImagesExtensions($allowedImagesExtensions)
|
||||||
|
{
|
||||||
|
$this->allowedImagesExtensions = $allowedImagesExtensions;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllowedImagesExtensions()
|
||||||
|
{
|
||||||
|
return $this->allowedImagesExtensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findFile($relatedFile)
|
||||||
|
{
|
||||||
|
$fileData = pathinfo($relatedFile);
|
||||||
|
$possibleFiles = [];
|
||||||
|
|
||||||
|
$extensions = $this->getAllowedImagesExtensions();
|
||||||
|
|
||||||
|
foreach ($extensions as $ext) {
|
||||||
|
$possibleFiles[] = $fileData['dirname'] . '/' . $fileData['filename'] . '.' . $ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($possibleFiles as $file) {
|
||||||
|
if (file_exists($file)) {
|
||||||
|
return $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
60
is_themecore/src/Core/Webp/WebpConvertLibraries.php
Normal file
60
is_themecore/src/Core/Webp/WebpConvertLibraries.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\Webp;
|
||||||
|
|
||||||
|
use WebPConvert\Convert\ConverterFactory;
|
||||||
|
use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\ConverterNotFoundException;
|
||||||
|
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||||
|
|
||||||
|
class WebpConvertLibraries
|
||||||
|
{
|
||||||
|
protected $converters = [
|
||||||
|
'cwebp' => ['label' => 'Cwebp binary'],
|
||||||
|
'vips' => ['label' => 'Vips PHP extension'],
|
||||||
|
'imagick' => ['label' => 'Imagick PHP extension'],
|
||||||
|
'gmagick' => ['label' => 'Gmagick PHP extension'],
|
||||||
|
'imagemagick' => ['label' => 'Imagemagick binary'],
|
||||||
|
'graphicsmagick' => ['label' => 'Graphicsmagick binary (gm)'],
|
||||||
|
'gd' => ['label' => 'Gd PHP extension'],
|
||||||
|
// NOT SUPPORTED
|
||||||
|
// 'ewww' => ['label' => 'EWWW cloud service'],
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $exampleImgFile = _PS_MODULE_DIR_ . 'is_themecore/views/img/example.jpg';
|
||||||
|
protected $exampleImgFileDesc = _PS_MODULE_DIR_ . 'is_themecore/views/img/example.webp';
|
||||||
|
|
||||||
|
public function getConvertersList(): array
|
||||||
|
{
|
||||||
|
$converters = $this->converters;
|
||||||
|
|
||||||
|
foreach ($converters as $converterId => $converterOptions) {
|
||||||
|
$converters[$converterId]['id'] = $converterId;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$converterInstance = ConverterFactory::makeConverter($converterId, $this->exampleImgFile, $this->exampleImgFileDesc, []);
|
||||||
|
$converterInstance->checkOperationality();
|
||||||
|
$converterInstance->doConvert();
|
||||||
|
$converters[$converterId]['disabled'] = false;
|
||||||
|
} catch (ConversionFailedException $conversionFailedException) {
|
||||||
|
$converters[$converterId]['disabled'] = true;
|
||||||
|
} catch (ConverterNotFoundException $converterNotFoundException) {
|
||||||
|
$converters[$converterId]['disabled'] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $converters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFirstAvailableConverter(): array
|
||||||
|
{
|
||||||
|
$list = $this->getConvertersList();
|
||||||
|
|
||||||
|
foreach ($list as $converter) {
|
||||||
|
if (!$converter['disabled']) {
|
||||||
|
return $converter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
79
is_themecore/src/Core/Webp/WebpFilesEraser.php
Normal file
79
is_themecore/src/Core/Webp/WebpFilesEraser.php
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\Webp;
|
||||||
|
|
||||||
|
use Symfony\Component\Finder\Finder;
|
||||||
|
|
||||||
|
class WebpFilesEraser
|
||||||
|
{
|
||||||
|
private $query = '';
|
||||||
|
private $finder;
|
||||||
|
private $files;
|
||||||
|
private $excludeList = ['node_modules', 'vendor', 'app', 'var', 'classes', 'controllers', 'download'];
|
||||||
|
private $filesCount = 0;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->finder = new Finder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setQuery($query)
|
||||||
|
{
|
||||||
|
$this->query = $query;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQuery()
|
||||||
|
{
|
||||||
|
return $this->query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setExcludeList(array $excludeList)
|
||||||
|
{
|
||||||
|
$this->excludeList = $excludeList;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExcludeList()
|
||||||
|
{
|
||||||
|
return $this->excludeList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setFilesCount()
|
||||||
|
{
|
||||||
|
$this->filesCount = iterator_count($this->files);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFilesCount()
|
||||||
|
{
|
||||||
|
return $this->filesCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function findFiles()
|
||||||
|
{
|
||||||
|
$this->files = $this->finder
|
||||||
|
->files()
|
||||||
|
->ignoreUnreadableDirs()
|
||||||
|
->in($this->query)
|
||||||
|
->exclude($this->excludeList)
|
||||||
|
->name('*.webp');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function eraseFiles()
|
||||||
|
{
|
||||||
|
$this->findFiles();
|
||||||
|
$this->setFilesCount();
|
||||||
|
|
||||||
|
foreach ($this->files as $file) {
|
||||||
|
try {
|
||||||
|
unlink($file->getPathname());
|
||||||
|
} catch (\Throwable $error) {
|
||||||
|
throw $error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
110
is_themecore/src/Core/Webp/WebpGenerator.php
Normal file
110
is_themecore/src/Core/Webp/WebpGenerator.php
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\Webp;
|
||||||
|
|
||||||
|
use WebPConvert\WebPConvert;
|
||||||
|
|
||||||
|
class WebpGenerator
|
||||||
|
{
|
||||||
|
protected $fileFinder;
|
||||||
|
protected $destinationFile = '';
|
||||||
|
protected $converter = false;
|
||||||
|
protected $debugEnabled = false;
|
||||||
|
protected $sharpYuv = false;
|
||||||
|
protected $quality = 90;
|
||||||
|
|
||||||
|
public function __construct(RelatedImageFileFinder $fileFinder)
|
||||||
|
{
|
||||||
|
$this->fileFinder = $fileFinder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setQuality($quality)
|
||||||
|
{
|
||||||
|
$this->quality = $quality;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQuality(): int
|
||||||
|
{
|
||||||
|
return $this->quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setConverter($converter)
|
||||||
|
{
|
||||||
|
$this->converter = $converter;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConverter(): string
|
||||||
|
{
|
||||||
|
return $this->converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSharpYuv($sharpYuv)
|
||||||
|
{
|
||||||
|
$this->sharpYuv = $sharpYuv;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSharpYuv(): bool
|
||||||
|
{
|
||||||
|
return $this->sharpYuv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDebugEnabled($debugEnabled)
|
||||||
|
{
|
||||||
|
$this->debugEnabled = $debugEnabled;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDebugEnabled(): bool
|
||||||
|
{
|
||||||
|
return $this->debugEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDestinationFile($destinationFile)
|
||||||
|
{
|
||||||
|
$this->destinationFile = $destinationFile;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDestinationFile(): string
|
||||||
|
{
|
||||||
|
return $this->destinationFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findRelatedFile()
|
||||||
|
{
|
||||||
|
return $this->fileFinder->findFile($this->getDestinationFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function convertAndServe()
|
||||||
|
{
|
||||||
|
$sourceFile = $this->findRelatedFile();
|
||||||
|
|
||||||
|
WebPConvert::serveConverted($sourceFile, $this->destinationFile, [
|
||||||
|
'fail' => 'original',
|
||||||
|
'show-report' => $this->getDebugEnabled(),
|
||||||
|
|
||||||
|
'serve-image' => [
|
||||||
|
'headers' => [
|
||||||
|
'cache-control' => true,
|
||||||
|
'vary-accept' => true,
|
||||||
|
// other headers can be toggled...
|
||||||
|
],
|
||||||
|
'cache-control-header' => 'max-age=2',
|
||||||
|
],
|
||||||
|
'convert' => [
|
||||||
|
'stack-converters' => [$this->getConverter()],
|
||||||
|
'quality' => $this->getQuality(),
|
||||||
|
'encoding' => 'auto',
|
||||||
|
'sharp-yuv' => $this->getSharpYuv(),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
109
is_themecore/src/Core/Webp/WebpPictureGenerator.php
Normal file
109
is_themecore/src/Core/Webp/WebpPictureGenerator.php
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Core\Webp;
|
||||||
|
|
||||||
|
class WebpPictureGenerator
|
||||||
|
{
|
||||||
|
private $allowedExtensions = ['png', 'jpg', 'jpeg'];
|
||||||
|
protected $content = '';
|
||||||
|
private $doc;
|
||||||
|
|
||||||
|
public function __construct($content)
|
||||||
|
{
|
||||||
|
$this->content = $content;
|
||||||
|
$this->doc = new \DOMDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadContent()
|
||||||
|
{
|
||||||
|
$this->doc->loadHTML('<?xml encoding="utf-8" ?>' . $this->content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generatePictureTags(): void
|
||||||
|
{
|
||||||
|
$images = $this->doc->getElementsByTagName('img');
|
||||||
|
|
||||||
|
if (0 === count($images)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($images as $image) {
|
||||||
|
if ($image->hasAttribute('data-external-url')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->generatePictureTagFromImg($image);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->content = $this->doc->saveHTML();
|
||||||
|
$this->content = str_replace('<?xml encoding="utf-8" ?>', '', $this->content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generatePictureTagFromImg($image)
|
||||||
|
{
|
||||||
|
$lazyLoad = !empty($params['lazyload']) ? $params['lazyload'] : (bool) preg_match('/' . implode('|', ['lazyload', 'swiper-lazy']) . '/i', $image->ownerDocument->saveHTML($image));
|
||||||
|
$srcAttributePrefix = $lazyLoad ? 'data-' : '';
|
||||||
|
$containSrcset = $image->hasAttribute($srcAttributePrefix . 'srcset');
|
||||||
|
$srcAttribute = $srcAttributePrefix . ($containSrcset ? 'srcset' : 'src');
|
||||||
|
|
||||||
|
$src = $image->getAttribute($srcAttribute);
|
||||||
|
$rawSrcArray = explode(',', $src);
|
||||||
|
$imageSrcArray = [];
|
||||||
|
|
||||||
|
foreach ($rawSrcArray as $rawSrc) {
|
||||||
|
$srcWithMediaArray = explode(' ', $rawSrc);
|
||||||
|
|
||||||
|
$srcWithMediaArray = array_values(array_filter($srcWithMediaArray, function ($elem) {
|
||||||
|
return !empty($elem);
|
||||||
|
}));
|
||||||
|
|
||||||
|
$imageSrcArray[] = [
|
||||||
|
'file' => $srcWithMediaArray[0] ?? null,
|
||||||
|
'media' => $srcWithMediaArray[1] ?? null,
|
||||||
|
'ext' => isset($srcWithMediaArray[0]) ? pathinfo($srcWithMediaArray[0], PATHINFO_EXTENSION) : null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$picture = $this->doc->createElement('picture');
|
||||||
|
$pict_clone = $picture->cloneNode();
|
||||||
|
$image->parentNode->replaceChild($pict_clone, $image);
|
||||||
|
$pict_clone->appendChild($image);
|
||||||
|
|
||||||
|
$source = $this->doc->createElement('source');
|
||||||
|
$source->setAttribute('type', 'image/webp');
|
||||||
|
$sourceWebp = '';
|
||||||
|
|
||||||
|
$lastKey = array_key_last($imageSrcArray);
|
||||||
|
|
||||||
|
foreach ($imageSrcArray as $key => $imageSrc) {
|
||||||
|
$ext = explode('?', $imageSrc['ext']);
|
||||||
|
$ext = $ext[0] ?? null;
|
||||||
|
|
||||||
|
if (!in_array($ext, $this->allowedExtensions)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$newWebpSrc = str_replace('.' . $imageSrc['ext'], '.webp', $imageSrc['file']);
|
||||||
|
|
||||||
|
$sourceWebp .= $newWebpSrc . ($imageSrc['media'] ? ' ' . $imageSrc['media'] : '');
|
||||||
|
|
||||||
|
if ($key != $lastKey) {
|
||||||
|
$sourceWebp .= ', ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sourceWebp) {
|
||||||
|
$source->setAttribute($lazyLoad ? 'data-srcset' : 'srcset', $sourceWebp);
|
||||||
|
$src_clone = $source->cloneNode();
|
||||||
|
$image->parentNode->replaceChild($src_clone, $image);
|
||||||
|
$src_clone->appendChild($image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContent(): string
|
||||||
|
{
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
is_themecore/src/Core/Webp/index.php
Normal file
11
is_themecore/src/Core/Webp/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
11
is_themecore/src/Core/index.php
Normal file
11
is_themecore/src/Core/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Form\ChoiceProvider;
|
||||||
|
|
||||||
|
use Oksydan\Module\IsThemeCore\Core\ListingDisplay\ThemeListDisplay;
|
||||||
|
use PrestaShop\PrestaShop\Core\Form\FormChoiceProviderInterface;
|
||||||
|
|
||||||
|
class ListDisplayChoiceProvider implements FormChoiceProviderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ThemeListDisplay
|
||||||
|
*/
|
||||||
|
protected $themeListDisplay;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ThemeListDisplay $themeListDisplay
|
||||||
|
*/
|
||||||
|
public function __construct(ThemeListDisplay $themeListDisplay)
|
||||||
|
{
|
||||||
|
$this->themeListDisplay = $themeListDisplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getChoices(): array
|
||||||
|
{
|
||||||
|
$choices = [];
|
||||||
|
|
||||||
|
foreach ($this->themeListDisplay->getDisplayOptions() as $display) {
|
||||||
|
$choices[$display] = $display;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $choices;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Form\ChoiceProvider;
|
||||||
|
|
||||||
|
use Oksydan\Module\IsThemeCore\Core\Webp\WebpConvertLibraries;
|
||||||
|
use PrestaShop\PrestaShop\Core\Form\FormChoiceProviderInterface;
|
||||||
|
|
||||||
|
class WebpLibraryChoiceProvider implements FormChoiceProviderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var WebpConvertLibraries
|
||||||
|
*/
|
||||||
|
protected $webpConvertLibraries;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param WebpConvertLibraries $webpConvertLibraries
|
||||||
|
*/
|
||||||
|
public function __construct(WebpConvertLibraries $webpConvertLibraries)
|
||||||
|
{
|
||||||
|
$this->webpConvertLibraries = $webpConvertLibraries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getChoices(): array
|
||||||
|
{
|
||||||
|
$choices = [];
|
||||||
|
|
||||||
|
foreach ($this->webpConvertLibraries->getConvertersList() as $converter) {
|
||||||
|
$choices[$converter['label']] = $converter['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $choices;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getChoicesFull(): array
|
||||||
|
{
|
||||||
|
$choices = [];
|
||||||
|
|
||||||
|
foreach ($this->webpConvertLibraries->getConvertersList() as $converter) {
|
||||||
|
$choices[$converter['id']] = $converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $choices;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
is_themecore/src/Form/ChoiceProvider/index.php
Normal file
11
is_themecore/src/Form/ChoiceProvider/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
132
is_themecore/src/Form/Settings/GeneralConfiguration.php
Normal file
132
is_themecore/src/Form/Settings/GeneralConfiguration.php
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Form\Settings;
|
||||||
|
|
||||||
|
use PrestaShop\PrestaShop\Core\Configuration\AbstractMultistoreConfiguration;
|
||||||
|
use PrestaShopBundle\Service\Form\MultistoreCheckboxEnabler;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration is used to save data to configuration table and retrieve from it
|
||||||
|
*/
|
||||||
|
final class GeneralConfiguration extends AbstractMultistoreConfiguration
|
||||||
|
{
|
||||||
|
private const CONFIGURATION_FIELDS = [
|
||||||
|
'list_display_settings',
|
||||||
|
'early_hints',
|
||||||
|
'preload_css',
|
||||||
|
'load_party_town',
|
||||||
|
'debug_party_town',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public const THEMECORE_DISPLAY_LIST = 'THEMECORE_DISPLAY_LIST';
|
||||||
|
public const THEMECORE_EARLY_HINTS = 'THEMECORE_EARLY_HINTS';
|
||||||
|
public const THEMECORE_PRELOAD_CSS = 'THEMECORE_PRELOAD_CSS';
|
||||||
|
public const THEMECORE_LOAD_PARTY_TOWN = 'THEMECORE_LOAD_PARTY_TOWN';
|
||||||
|
public const THEMECORE_DEBUG_PARTY_TOWN = 'THEMECORE_DEBUG_PARTY_TOWN';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
|
private array $fields = [
|
||||||
|
'list_display_settings' => self::THEMECORE_DISPLAY_LIST,
|
||||||
|
'early_hints' => self::THEMECORE_EARLY_HINTS,
|
||||||
|
'preload_css' => self::THEMECORE_PRELOAD_CSS,
|
||||||
|
'load_party_town' => self::THEMECORE_LOAD_PARTY_TOWN,
|
||||||
|
'debug_party_town' => self::THEMECORE_DEBUG_PARTY_TOWN,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function getConfiguration(): array
|
||||||
|
{
|
||||||
|
$configurationValues = [];
|
||||||
|
|
||||||
|
foreach ($this->fields as $field => $configurationKey) {
|
||||||
|
$configurationValues[$field] = $this->configuration->get($configurationKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $configurationValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $configuration
|
||||||
|
*
|
||||||
|
* @return array<int, array<string, mixed>>
|
||||||
|
*/
|
||||||
|
public function updateConfiguration(array $configuration): array
|
||||||
|
{
|
||||||
|
$errors = [];
|
||||||
|
|
||||||
|
if (!$this->validateConfiguration($configuration)) {
|
||||||
|
$errors[] = [
|
||||||
|
'key' => 'Invalid configuration',
|
||||||
|
'parameters' => [],
|
||||||
|
'domain' => 'Admin.Notifications.Warning',
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$shopConstraint = $this->getShopConstraint();
|
||||||
|
|
||||||
|
try {
|
||||||
|
foreach ($this->fields as $field => $configurationKey) {
|
||||||
|
$this->updateConfigurationValue($configurationKey, $field, $configuration, $shopConstraint);
|
||||||
|
}
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
$errors[] = [
|
||||||
|
'key' => $exception->getMessage(),
|
||||||
|
'parameters' => [],
|
||||||
|
'domain' => 'Admin.Notifications.Warning',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure the parameters passed are valid.
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $configuration
|
||||||
|
*
|
||||||
|
* @return bool Returns true if no exception are thrown
|
||||||
|
*/
|
||||||
|
public function validateConfiguration(array $configuration): bool
|
||||||
|
{
|
||||||
|
foreach ($this->fields as $field => $configurationKey) {
|
||||||
|
$multistoreKey = MultistoreCheckboxEnabler::MULTISTORE_FIELD_PREFIX . $field;
|
||||||
|
$this->fields[$multistoreKey] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($configuration as $key => $value) {
|
||||||
|
if (!key_exists($key, $this->fields)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return OptionsResolver
|
||||||
|
*/
|
||||||
|
protected function buildResolver(): OptionsResolver
|
||||||
|
{
|
||||||
|
return (new OptionsResolver())
|
||||||
|
->setDefined(self::CONFIGURATION_FIELDS)
|
||||||
|
->setAllowedTypes('list_display_settings', ['string', 'null'])
|
||||||
|
->setAllowedTypes('early_hints', 'bool')
|
||||||
|
->setAllowedTypes('preload_css', 'bool')
|
||||||
|
->setAllowedTypes('load_party_town', 'bool')
|
||||||
|
->setAllowedTypes('debug_party_town', 'bool');
|
||||||
|
}
|
||||||
|
}
|
||||||
49
is_themecore/src/Form/Settings/GeneralFormDataProvider.php
Normal file
49
is_themecore/src/Form/Settings/GeneralFormDataProvider.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Form\Settings;
|
||||||
|
|
||||||
|
use PrestaShop\PrestaShop\Core\Configuration\DataConfigurationInterface;
|
||||||
|
use PrestaShop\PrestaShop\Core\Form\FormDataProviderInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class GeneralFormDataProvider
|
||||||
|
*/
|
||||||
|
class GeneralFormDataProvider implements FormDataProviderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var DataConfigurationInterface
|
||||||
|
*/
|
||||||
|
private $generalConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DataConfigurationInterface $generalConfiguration
|
||||||
|
*/
|
||||||
|
public function __construct(DataConfigurationInterface $generalConfiguration)
|
||||||
|
{
|
||||||
|
$this->generalConfiguration = $generalConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return array<string, mixed> The form data as an associative array
|
||||||
|
*/
|
||||||
|
public function getData(): array
|
||||||
|
{
|
||||||
|
return $this->generalConfiguration->getConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $data
|
||||||
|
*
|
||||||
|
* @return array<int, array<string, mixed>> An array of errors messages if data can't persisted
|
||||||
|
*/
|
||||||
|
public function setData(array $data): array
|
||||||
|
{
|
||||||
|
return $this->generalConfiguration->updateConfiguration($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
104
is_themecore/src/Form/Settings/GeneralType.php
Normal file
104
is_themecore/src/Form/Settings/GeneralType.php
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Form\Settings;
|
||||||
|
|
||||||
|
use PrestaShopBundle\Form\Admin\Type\MultistoreConfigurationType;
|
||||||
|
use PrestaShopBundle\Form\Admin\Type\SwitchType;
|
||||||
|
use PrestaShopBundle\Form\Admin\Type\TranslatorAwareType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
class GeneralType extends TranslatorAwareType
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var TranslatorInterface
|
||||||
|
*/
|
||||||
|
private $translator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $displayListChoices;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GeneralType constructor.
|
||||||
|
*
|
||||||
|
* @param TranslatorInterface $translator
|
||||||
|
* @param array $locales
|
||||||
|
* @param array $displayListChoices
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
TranslatorInterface $translator,
|
||||||
|
array $locales,
|
||||||
|
array $displayListChoices
|
||||||
|
) {
|
||||||
|
parent::__construct($translator, $locales);
|
||||||
|
$this->displayListChoices = $displayListChoices;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @param FormBuilderInterface<string, mixed> $builder
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('list_display_settings',
|
||||||
|
ChoiceType::class,
|
||||||
|
[
|
||||||
|
'choices' => $this->displayListChoices,
|
||||||
|
'label' => $this->trans('Default list display', 'Modules.isthemecore.Admin'),
|
||||||
|
'multistore_configuration_key' => GeneralConfiguration::THEMECORE_DISPLAY_LIST,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
->add('early_hints',
|
||||||
|
SwitchType::class,
|
||||||
|
[
|
||||||
|
'required' => false,
|
||||||
|
'label' => $this->trans('Early hints (HTTP 103) enabled', 'Modules.isthemecore.Admin'),
|
||||||
|
'help' => $this->trans('Cloudflare CDN, Early hints option have to enabled. <a href="https://developers.cloudflare.com/cache/about/early-hints/">More information</a>', 'Modules.isthemecore.Admin'),
|
||||||
|
'multistore_configuration_key' => GeneralConfiguration::THEMECORE_EARLY_HINTS,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
->add('preload_css',
|
||||||
|
SwitchType::class,
|
||||||
|
[
|
||||||
|
'required' => false,
|
||||||
|
'label' => $this->trans('Preload css enabled, only working with CCC for css option enabled', 'Modules.isthemecore.Admin'),
|
||||||
|
'multistore_configuration_key' => GeneralConfiguration::THEMECORE_PRELOAD_CSS,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
->add('load_party_town',
|
||||||
|
SwitchType::class,
|
||||||
|
[
|
||||||
|
'required' => false,
|
||||||
|
'label' => $this->trans('Load partytown script', 'Modules.isthemecore.Admin'),
|
||||||
|
'help' => $this->trans('Be aware that partytown is still beta. Make sure that everything is working as expected before pushing it to your production store.', 'Modules.isthemecore.Admin'),
|
||||||
|
'multistore_configuration_key' => GeneralConfiguration::THEMECORE_LOAD_PARTY_TOWN,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
->add('debug_party_town',
|
||||||
|
SwitchType::class,
|
||||||
|
[
|
||||||
|
'required' => false,
|
||||||
|
'label' => $this->trans('Enable debug mode for partytown', 'Modules.isthemecore.Admin'),
|
||||||
|
'multistore_configuration_key' => GeneralConfiguration::THEMECORE_DEBUG_PARTY_TOWN,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @see MultistoreConfigurationTypeExtension
|
||||||
|
*/
|
||||||
|
public function getParent(): string
|
||||||
|
{
|
||||||
|
return MultistoreConfigurationType::class;
|
||||||
|
}
|
||||||
|
}
|
||||||
131
is_themecore/src/Form/Settings/WebpConfiguration.php
Normal file
131
is_themecore/src/Form/Settings/WebpConfiguration.php
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Form\Settings;
|
||||||
|
|
||||||
|
use PrestaShop\PrestaShop\Adapter\Configuration;
|
||||||
|
use PrestaShop\PrestaShop\Core\Configuration\DataConfigurationInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration is used to save data to configuration table and retrieve from it
|
||||||
|
*/
|
||||||
|
final class WebpConfiguration implements DataConfigurationInterface
|
||||||
|
{
|
||||||
|
private const CONFIGURATION_FIELDS = [
|
||||||
|
'webp_enabled',
|
||||||
|
'webp_quality',
|
||||||
|
'webp_converter',
|
||||||
|
'webp_sharpyuv',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public const THEMECORE_WEBP_ENABLED = 'THEMECORE_WEBP_ENABLED';
|
||||||
|
public const THEMECORE_WEBP_QUALITY = 'THEMECORE_WEBP_QUALITY';
|
||||||
|
public const THEMECORE_WEBP_CONVERTER = 'THEMECORE_WEBP_CONVERTER';
|
||||||
|
public const THEMECORE_WEBP_SHARPYUV = 'THEMECORE_WEBP_SHARPYUV';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
|
private $fields = [
|
||||||
|
'webp_enabled' => self::THEMECORE_WEBP_ENABLED,
|
||||||
|
'webp_quality' => self::THEMECORE_WEBP_QUALITY,
|
||||||
|
'webp_converter' => self::THEMECORE_WEBP_CONVERTER,
|
||||||
|
'webp_sharpyuv' => self::THEMECORE_WEBP_SHARPYUV,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Configuration
|
||||||
|
*/
|
||||||
|
protected $configuration;
|
||||||
|
|
||||||
|
public function __construct(Configuration $configuration)
|
||||||
|
{
|
||||||
|
$this->configuration = $configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function getConfiguration(): array
|
||||||
|
{
|
||||||
|
$configurationValues = [];
|
||||||
|
|
||||||
|
foreach ($this->fields as $field => $configurationKey) {
|
||||||
|
$configurationValues[$field] = $this->configuration->get($configurationKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $configurationValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $configuration
|
||||||
|
*
|
||||||
|
* @return array<int, array<string, mixed>>
|
||||||
|
*/
|
||||||
|
public function updateConfiguration(array $configuration): array
|
||||||
|
{
|
||||||
|
$errors = [];
|
||||||
|
|
||||||
|
if (!$this->validateConfiguration($configuration)) {
|
||||||
|
$errors[] = [
|
||||||
|
'key' => 'Invalid configuration',
|
||||||
|
'parameters' => [],
|
||||||
|
'domain' => 'Admin.Notifications.Warning',
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
foreach ($this->fields as $field => $configurationKey) {
|
||||||
|
$this->configuration->set($configurationKey, $configuration[$field]);
|
||||||
|
}
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
$errors[] = [
|
||||||
|
'key' => $exception->getMessage(),
|
||||||
|
'parameters' => [],
|
||||||
|
'domain' => 'Admin.Notifications.Warning',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure the parameters passed are valid.
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $configuration
|
||||||
|
*
|
||||||
|
* @return bool Returns true if no exception are thrown
|
||||||
|
*/
|
||||||
|
public function validateConfiguration(array $configuration): bool
|
||||||
|
{
|
||||||
|
foreach ($configuration as $key => $value) {
|
||||||
|
if (!key_exists($key, $this->fields)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return OptionsResolver
|
||||||
|
*/
|
||||||
|
protected function buildResolver(): OptionsResolver
|
||||||
|
{
|
||||||
|
return (new OptionsResolver())
|
||||||
|
->setDefined(self::CONFIGURATION_FIELDS)
|
||||||
|
->setAllowedTypes('webp_enabled', 'bool')
|
||||||
|
->setAllowedTypes('webp_quality', 'string')
|
||||||
|
->setAllowedTypes('webp_converter', 'string')
|
||||||
|
->setAllowedTypes('webp_sharpyuv', 'bool');
|
||||||
|
}
|
||||||
|
}
|
||||||
49
is_themecore/src/Form/Settings/WebpFormDataProvider.php
Normal file
49
is_themecore/src/Form/Settings/WebpFormDataProvider.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Form\Settings;
|
||||||
|
|
||||||
|
use PrestaShop\PrestaShop\Core\Configuration\DataConfigurationInterface;
|
||||||
|
use PrestaShop\PrestaShop\Core\Form\FormDataProviderInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class WebpFormDataProvider
|
||||||
|
*/
|
||||||
|
class WebpFormDataProvider implements FormDataProviderInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var DataConfigurationInterface
|
||||||
|
*/
|
||||||
|
private $webpConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DataConfigurationInterface $webpConfiguration
|
||||||
|
*/
|
||||||
|
public function __construct(DataConfigurationInterface $webpConfiguration)
|
||||||
|
{
|
||||||
|
$this->webpConfiguration = $webpConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return array<string, mixed> The form data as an associative array
|
||||||
|
*/
|
||||||
|
public function getData(): array
|
||||||
|
{
|
||||||
|
return $this->webpConfiguration->getConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $data
|
||||||
|
*
|
||||||
|
* @return array<int, array<string, mixed>> An array of errors messages if data can't persisted
|
||||||
|
*/
|
||||||
|
public function setData(array $data): array
|
||||||
|
{
|
||||||
|
return $this->webpConfiguration->updateConfiguration($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
247
is_themecore/src/Form/Settings/WebpType.php
Normal file
247
is_themecore/src/Form/Settings/WebpType.php
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Form\Settings;
|
||||||
|
|
||||||
|
use PrestaShopBundle\Form\Admin\Type\IconButtonType;
|
||||||
|
use PrestaShopBundle\Form\Admin\Type\MultistoreConfigurationType;
|
||||||
|
use PrestaShopBundle\Form\Admin\Type\SwitchType;
|
||||||
|
use PrestaShopBundle\Form\Admin\Type\TranslatorAwareType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\Routing\RouterInterface;
|
||||||
|
use Symfony\Component\Translation\TranslatorInterface;
|
||||||
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
|
use Symfony\Component\Validator\Constraints\Range;
|
||||||
|
|
||||||
|
class WebpType extends TranslatorAwareType
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var TranslatorInterface
|
||||||
|
*/
|
||||||
|
private $translator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $convertersList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $convertersListFull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RouterInterface
|
||||||
|
*/
|
||||||
|
private $router;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebpType constructor.
|
||||||
|
*
|
||||||
|
* @param TranslatorInterface $translator
|
||||||
|
* @param array $locales
|
||||||
|
* @param array $convertersList
|
||||||
|
* @param array $convertersListFull
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
TranslatorInterface $translator,
|
||||||
|
array $locales,
|
||||||
|
array $convertersList,
|
||||||
|
array $convertersListFull,
|
||||||
|
RouterInterface $router
|
||||||
|
) {
|
||||||
|
parent::__construct($translator, $locales);
|
||||||
|
$this->convertersList = $convertersList;
|
||||||
|
$this->convertersListFull = $convertersListFull;
|
||||||
|
$this->router = $router;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function allWebpConvertersDisabled(): bool
|
||||||
|
{
|
||||||
|
return array_reduce($this->convertersListFull, function ($carry, $item) {
|
||||||
|
return $carry && $item['disabled'];
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @param FormBuilderInterface<string, mixed> $builder
|
||||||
|
* @param array<string, mixed> $options
|
||||||
|
*/
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
|
{
|
||||||
|
$webpDisabled = $this->allWebpConvertersDisabled();
|
||||||
|
$extraAttributes = [];
|
||||||
|
|
||||||
|
if ($webpDisabled) {
|
||||||
|
$extraAttributes = [
|
||||||
|
'alert_message' => $this->trans('Webp converters not available contact your admin or hosting provider.', 'Modules.isthemecore.Admin'),
|
||||||
|
'alert_type' => 'danger',
|
||||||
|
'alert_position' => 'append',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$builder
|
||||||
|
->add('webp_enabled',
|
||||||
|
SwitchType::class,
|
||||||
|
array_merge(
|
||||||
|
[
|
||||||
|
'required' => false,
|
||||||
|
'label' => $this->trans('Enable WEBP', 'Modules.isthemecore.Admin'),
|
||||||
|
'disabled' => $webpDisabled,
|
||||||
|
],
|
||||||
|
$extraAttributes
|
||||||
|
)
|
||||||
|
)
|
||||||
|
->add('webp_sharpyuv',
|
||||||
|
SwitchType::class,
|
||||||
|
[
|
||||||
|
'required' => false,
|
||||||
|
'label' => $this->trans('Enable better RGB->YUV color conversion', 'Modules.isthemecore.Admin'),
|
||||||
|
'disabled' => $webpDisabled,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
->add('webp_quality',
|
||||||
|
TextType::class,
|
||||||
|
[
|
||||||
|
'required' => false,
|
||||||
|
'label' => $this->trans('Webp quality', 'Modules.isthemecore.Admin'),
|
||||||
|
'help' => $this->trans('Range 1-100', 'Modules.isthemecore.Admin'),
|
||||||
|
'disabled' => $webpDisabled,
|
||||||
|
'constraints' => [
|
||||||
|
$this->getRangeConstraint(1, 100),
|
||||||
|
$this->getNotBlankConstraint(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
)
|
||||||
|
->add('webp_converter',
|
||||||
|
ChoiceType::class,
|
||||||
|
[
|
||||||
|
'choices' => $this->convertersList,
|
||||||
|
'label' => $this->trans('Webp converter options', 'Modules.isthemecore.Admin'),
|
||||||
|
'disabled' => $webpDisabled,
|
||||||
|
'expanded' => true,
|
||||||
|
'multiple' => false,
|
||||||
|
'choice_attr' => function ($choice) {
|
||||||
|
return ['disabled' => $this->convertersListFull[$choice]['disabled']];
|
||||||
|
},
|
||||||
|
'choice_label' => function ($choice) {
|
||||||
|
return $this->convertersListFull[$choice]['label'] . ($this->convertersListFull[$choice]['disabled'] ? '<span class="ml-1 badge badge-danger">' . $this->trans('not available', 'Modules.isthemecore.Admin') . '</span>' : '');
|
||||||
|
},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
->add('erase_all_webp', IconButtonType::class, [
|
||||||
|
'label' => $this->trans('Erase all webp images', 'Modules.isthemecore.Admin'),
|
||||||
|
'type' => 'link',
|
||||||
|
'icon' => 'delete',
|
||||||
|
'attr' => [
|
||||||
|
'class' => 'btn-danger',
|
||||||
|
'href' => $this->router->generate(
|
||||||
|
'is_themecore_module_settings_webp_erase_all',
|
||||||
|
[
|
||||||
|
'type' => 'all',
|
||||||
|
]
|
||||||
|
),
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->add('erase_product_webp', IconButtonType::class, [
|
||||||
|
'label' => $this->trans('Erase all product webp images', 'Modules.isthemecore.Admin'),
|
||||||
|
'type' => 'link',
|
||||||
|
'icon' => 'delete',
|
||||||
|
'attr' => [
|
||||||
|
'class' => 'btn-danger',
|
||||||
|
'href' => $this->router->generate(
|
||||||
|
'is_themecore_module_settings_webp_erase_all',
|
||||||
|
[
|
||||||
|
'type' => 'product',
|
||||||
|
]
|
||||||
|
),
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->add('erase_modules_webp', IconButtonType::class, [
|
||||||
|
'label' => $this->trans('Erase all modules webp images', 'Modules.isthemecore.Admin'),
|
||||||
|
'type' => 'link',
|
||||||
|
'icon' => 'delete',
|
||||||
|
'attr' => [
|
||||||
|
'class' => 'btn-danger',
|
||||||
|
'href' => $this->router->generate(
|
||||||
|
'is_themecore_module_settings_webp_erase_all',
|
||||||
|
[
|
||||||
|
'type' => 'module',
|
||||||
|
]
|
||||||
|
),
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->add('erase_cms_webp', IconButtonType::class, [
|
||||||
|
'label' => $this->trans('Erase all CMS webp images', 'Modules.isthemecore.Admin'),
|
||||||
|
'type' => 'link',
|
||||||
|
'icon' => 'delete',
|
||||||
|
'attr' => [
|
||||||
|
'class' => 'btn-danger',
|
||||||
|
'href' => $this->router->generate(
|
||||||
|
'is_themecore_module_settings_webp_erase_all',
|
||||||
|
[
|
||||||
|
'type' => 'cms',
|
||||||
|
]
|
||||||
|
),
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->add('erase_themes_webp', IconButtonType::class, [
|
||||||
|
'label' => $this->trans('Erase all themes webp images', 'Modules.isthemecore.Admin'),
|
||||||
|
'type' => 'link',
|
||||||
|
'icon' => 'delete',
|
||||||
|
'attr' => [
|
||||||
|
'class' => 'btn-danger',
|
||||||
|
'href' => $this->router->generate(
|
||||||
|
'is_themecore_module_settings_webp_erase_all',
|
||||||
|
[
|
||||||
|
'type' => 'themes',
|
||||||
|
]
|
||||||
|
),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @see MultistoreConfigurationTypeExtension
|
||||||
|
*/
|
||||||
|
public function getParent(): string
|
||||||
|
{
|
||||||
|
return MultistoreConfigurationType::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return NotBlank
|
||||||
|
*/
|
||||||
|
private function getNotBlankConstraint()
|
||||||
|
{
|
||||||
|
return new NotBlank([
|
||||||
|
'message' => $this->trans('This field cannot be empty.', 'Modules.isthemecore.Admin'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Range
|
||||||
|
*/
|
||||||
|
private function getRangeConstraint(int $min = 1, int $max = 100)
|
||||||
|
{
|
||||||
|
return new Range([
|
||||||
|
'min' => $min,
|
||||||
|
'max' => $max,
|
||||||
|
'invalidMessage' => $this->trans(
|
||||||
|
'This field value have to be between %min% and %max%.',
|
||||||
|
'Modules.isthemecore.Admin',
|
||||||
|
[
|
||||||
|
'%min%' => $min,
|
||||||
|
'%max%' => $max,
|
||||||
|
]
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
is_themecore/src/Form/Settings/index.php
Normal file
11
is_themecore/src/Form/Settings/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
11
is_themecore/src/Form/index.php
Normal file
11
is_themecore/src/Form/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
25
is_themecore/src/Hook/AbstractHook.php
Normal file
25
is_themecore/src/Hook/AbstractHook.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Hook;
|
||||||
|
|
||||||
|
abstract class AbstractHook
|
||||||
|
{
|
||||||
|
public const HOOK_LIST = [];
|
||||||
|
|
||||||
|
protected $module;
|
||||||
|
protected $context;
|
||||||
|
|
||||||
|
public function __construct(\Is_themecore $module)
|
||||||
|
{
|
||||||
|
$this->module = $module;
|
||||||
|
$this->context = \Context::getContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getAvailableHooks()
|
||||||
|
{
|
||||||
|
return static::HOOK_LIST;
|
||||||
|
}
|
||||||
|
}
|
||||||
45
is_themecore/src/Hook/Assets.php
Normal file
45
is_themecore/src/Hook/Assets.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Hook;
|
||||||
|
|
||||||
|
use Oksydan\Module\IsThemeCore\Core\ThemeAssets\ThemeAssetConfigProvider;
|
||||||
|
use Oksydan\Module\IsThemeCore\Core\ThemeAssets\ThemeAssetsRegister;
|
||||||
|
|
||||||
|
class Assets extends AbstractHook
|
||||||
|
{
|
||||||
|
public const HOOK_LIST = [
|
||||||
|
'actionFrontControllerSetMedia',
|
||||||
|
'actionProductSearchAfter',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removing ps_faceted search module assets
|
||||||
|
*/
|
||||||
|
public function hookActionProductSearchAfter(): void
|
||||||
|
{
|
||||||
|
$this->context->controller->unregisterJavascript('facetedsearch_front');
|
||||||
|
$this->context->controller->unregisterStylesheet('facetedsearch_front');
|
||||||
|
|
||||||
|
$needsJQueryUi = \Module::isEnabled('pm_advancedsearch4') && $this->context->controller instanceof \ProductListingFrontController;
|
||||||
|
|
||||||
|
if (!$needsJQueryUi) {
|
||||||
|
$this->context->controller->unregisterJavascript('jquery-ui');
|
||||||
|
$this->context->controller->unregisterStylesheet('jquery-ui');
|
||||||
|
$this->context->controller->unregisterStylesheet('jquery-ui-theme');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hookActionFrontControllerSetMedia()
|
||||||
|
{
|
||||||
|
$assetsRegister = new ThemeAssetsRegister(
|
||||||
|
new ThemeAssetConfigProvider(_PS_THEME_DIR_),
|
||||||
|
$this->context
|
||||||
|
);
|
||||||
|
|
||||||
|
$assetsRegister->registerThemeAssets();
|
||||||
|
|
||||||
|
\Media::addJsDef([
|
||||||
|
'listDisplayAjaxUrl' => $this->context->link->getModuleLink($this->module->name, 'ajaxTheme'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
136
is_themecore/src/Hook/Header.php
Normal file
136
is_themecore/src/Hook/Header.php
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Hook;
|
||||||
|
|
||||||
|
use Oksydan\Module\IsThemeCore\Core\Breadcrumbs\ThemeBreadcrumbs;
|
||||||
|
use Oksydan\Module\IsThemeCore\Core\ListingDisplay\ThemeListDisplay;
|
||||||
|
use Oksydan\Module\IsThemeCore\Core\Partytown\PartytownScript;
|
||||||
|
use Oksydan\Module\IsThemeCore\Core\Partytown\PartytownScriptUriResolver;
|
||||||
|
use Oksydan\Module\IsThemeCore\Core\StructuredData\BreadcrumbStructuredData;
|
||||||
|
use Oksydan\Module\IsThemeCore\Core\StructuredData\ProductStructuredData;
|
||||||
|
use Oksydan\Module\IsThemeCore\Core\StructuredData\ShopStructuredData;
|
||||||
|
use Oksydan\Module\IsThemeCore\Core\StructuredData\StructuredDataInterface;
|
||||||
|
use Oksydan\Module\IsThemeCore\Core\StructuredData\WebsiteStructuredData;
|
||||||
|
use Oksydan\Module\IsThemeCore\Form\Settings\GeneralConfiguration;
|
||||||
|
use Oksydan\Module\IsThemeCore\Form\Settings\WebpConfiguration;
|
||||||
|
|
||||||
|
class Header extends AbstractHook
|
||||||
|
{
|
||||||
|
public const HOOK_LIST = [
|
||||||
|
'actionFrontControllerInitBefore',
|
||||||
|
'displayHeader',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function hookActionFrontControllerInitBefore(): void
|
||||||
|
{
|
||||||
|
$themeListDisplay = new ThemeListDisplay();
|
||||||
|
$this->context->smarty->assign([
|
||||||
|
'listingDisplayType' => $themeListDisplay->getDisplay(),
|
||||||
|
'preloadCss' => \Configuration::get(GeneralConfiguration::THEMECORE_PRELOAD_CSS),
|
||||||
|
'webpEnabled' => \Configuration::get(WebpConfiguration::THEMECORE_WEBP_ENABLED),
|
||||||
|
'loadPartytown' => (bool) \Configuration::get(GeneralConfiguration::THEMECORE_LOAD_PARTY_TOWN),
|
||||||
|
'debugPartytown' => (bool) \Configuration::get(GeneralConfiguration::THEMECORE_DEBUG_PARTY_TOWN),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hookDisplayHeader(): string
|
||||||
|
{
|
||||||
|
$themeListDisplay = new ThemeListDisplay();
|
||||||
|
$breadcrumbs = (new ThemeBreadcrumbs())->getBreadcrumb();
|
||||||
|
|
||||||
|
if ($breadcrumbs['count']) {
|
||||||
|
$this->context->smarty->assign([
|
||||||
|
'breadcrumb' => $breadcrumbs,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->context->smarty->assign([
|
||||||
|
'jsonData' => $this->getStructuredData(),
|
||||||
|
'partytownScript' => $this->getPartytownScript(),
|
||||||
|
'partytownScriptUri' => $this->getPartytownScriptUri(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $this->module->fetch('module:is_themecore/views/templates/hook/head.tpl');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPartytownScriptUri(): string
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$uriResolver = $this->module->get(PartytownScriptUriResolver::class);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$uriResolver = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($uriResolver) {
|
||||||
|
return $uriResolver->getScriptUri();
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPartytownScript(): string
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$partytownScript = $this->module->get(PartytownScript::class);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$partytownScript = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($partytownScript instanceof PartytownScript) {
|
||||||
|
return $partytownScript->getScriptContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getStructuredData(): array
|
||||||
|
{
|
||||||
|
$dataArray = [];
|
||||||
|
|
||||||
|
if ($this->context->controller instanceof \ProductControllerCore && $this->context->controller->getProduct()->id !== null) {
|
||||||
|
try {
|
||||||
|
$productData = $this->module->get(ProductStructuredData::class);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$productData = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($productData instanceof StructuredDataInterface) {
|
||||||
|
$dataArray[] = $productData->getFormattedData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$breadcrumbData = $this->module->get(BreadcrumbStructuredData::class);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$breadcrumbData = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($breadcrumbData instanceof StructuredDataInterface) {
|
||||||
|
$dataArray[] = $breadcrumbData->getFormattedData();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$shopData = $this->module->get(ShopStructuredData::class);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$shopData = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($shopData instanceof StructuredDataInterface) {
|
||||||
|
$dataArray[] = $shopData->getFormattedData();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->context->controller->getPageName() === 'index') {
|
||||||
|
try {
|
||||||
|
$website = $this->module->get(WebsiteStructuredData::class);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$website = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($website instanceof StructuredDataInterface) {
|
||||||
|
$dataArray[] = $website->getFormattedData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dataArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
36
is_themecore/src/Hook/Htaccess.php
Normal file
36
is_themecore/src/Hook/Htaccess.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Hook;
|
||||||
|
|
||||||
|
class Htaccess extends AbstractHook
|
||||||
|
{
|
||||||
|
public const HOOK_LIST = [
|
||||||
|
'actionHtaccessCreate',
|
||||||
|
'objectShopUrlAddAfter',
|
||||||
|
'objectShopUrlUpdateAfter',
|
||||||
|
'objectShopUrlDeleteAfter',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function hookActionHtaccessCreate()
|
||||||
|
{
|
||||||
|
$generator = $this->module->get('oksydan.module.is_themecore.core.htaccess.htaccess_generator');
|
||||||
|
|
||||||
|
$generator->generate();
|
||||||
|
$generator->writeFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hookObjectShopUrlAddAfter()
|
||||||
|
{
|
||||||
|
$this->hookActionHtaccessCreate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hookObjectShopUrlUpdateAfter()
|
||||||
|
{
|
||||||
|
$this->hookActionHtaccessCreate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hookObjectShopUrlDeleteAfter()
|
||||||
|
{
|
||||||
|
$this->hookActionHtaccessCreate();
|
||||||
|
}
|
||||||
|
}
|
||||||
111
is_themecore/src/Hook/HtmlOutput.php
Normal file
111
is_themecore/src/Hook/HtmlOutput.php
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Hook;
|
||||||
|
|
||||||
|
use Oksydan\Module\IsThemeCore\Form\Settings\GeneralConfiguration;
|
||||||
|
use Oksydan\Module\IsThemeCore\Form\Settings\WebpConfiguration;
|
||||||
|
|
||||||
|
class HtmlOutput extends AbstractHook
|
||||||
|
{
|
||||||
|
public const HOOK_LIST = [
|
||||||
|
'actionOutputHTMLBefore',
|
||||||
|
];
|
||||||
|
|
||||||
|
public const REL_LIST = [
|
||||||
|
'preload',
|
||||||
|
'preconnect',
|
||||||
|
];
|
||||||
|
|
||||||
|
public const PRELOAD_TYPES_TO_EARLY_HINT = [
|
||||||
|
'image',
|
||||||
|
'stylesheet',
|
||||||
|
// 'font', //disabled for now causing higher LCP and weird FOUC
|
||||||
|
];
|
||||||
|
|
||||||
|
private $headers = [];
|
||||||
|
|
||||||
|
public function hookActionOutputHTMLBefore(array $params): void
|
||||||
|
{
|
||||||
|
$earlyHintsEnabled = \Configuration::get(GeneralConfiguration::THEMECORE_EARLY_HINTS, false);
|
||||||
|
$webpEnabled = \Configuration::get(WebpConfiguration::THEMECORE_WEBP_ENABLED, false);
|
||||||
|
|
||||||
|
if (!$earlyHintsEnabled && !$webpEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$preConfig = libxml_use_internal_errors(true);
|
||||||
|
$html = $params['html'];
|
||||||
|
$doc = new \DOMDocument();
|
||||||
|
$doc->loadHTML(
|
||||||
|
'<meta http-equiv="Content-Type" content="charset=utf-8">' . $html,
|
||||||
|
LIBXML_NOERROR | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
|
||||||
|
);
|
||||||
|
$links = $doc->getElementsByTagName('link');
|
||||||
|
|
||||||
|
foreach ($links as $link) {
|
||||||
|
$rel = $link->hasAttribute('rel') ? $link->attributes->getNamedItem('rel')->nodeValue : false;
|
||||||
|
$as = $link->hasAttribute('as') ? $link->attributes->getNamedItem('as')->nodeValue : false;
|
||||||
|
|
||||||
|
if ($webpEnabled && $rel === 'preload' && $as === 'image') {
|
||||||
|
$newLink = $doc->createElement('link');
|
||||||
|
$src = urldecode($link->attributes->getNamedItem('href')->nodeValue);
|
||||||
|
|
||||||
|
$newLink->setAttribute('href', str_replace(['.png', '.jpg', '.jpeg'], '.webp', $src));
|
||||||
|
|
||||||
|
foreach ($link->attributes as $attribute) {
|
||||||
|
if ($attribute->nodeName !== 'href') {
|
||||||
|
$newLink->setAttribute($attribute->nodeName, $attribute->nodeValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$link->parentNode->replaceChild($newLink, $link);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($earlyHintsEnabled && in_array($rel, self::REL_LIST)) {
|
||||||
|
if (isset($newLink)) {
|
||||||
|
$link = $newLink;
|
||||||
|
unset($newLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($rel) {
|
||||||
|
case 'preload':
|
||||||
|
$this->handlePreloadFromNodeElement($link);
|
||||||
|
break;
|
||||||
|
case 'preconnect':
|
||||||
|
$this->handlePreconnectFromNodeElement($link);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($webpEnabled) {
|
||||||
|
$content = $doc->saveHTML();
|
||||||
|
$content = str_replace('<meta http-equiv="Content-Type" content="charset=utf-8">', '', $content);
|
||||||
|
$params['html'] = $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->headers)) {
|
||||||
|
header('Link: ' . implode(', ', $this->headers));
|
||||||
|
}
|
||||||
|
|
||||||
|
libxml_use_internal_errors($preConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handlePreloadFromNodeElement($nodeElement)
|
||||||
|
{
|
||||||
|
$preloadAs = $nodeElement->attributes->getNamedItem('as')->nodeValue;
|
||||||
|
|
||||||
|
if (in_array($preloadAs, self::PRELOAD_TYPES_TO_EARLY_HINT)) {
|
||||||
|
$url = $nodeElement->attributes->getNamedItem('href')->nodeValue;
|
||||||
|
|
||||||
|
$this->headers[] = "<$url>; rel=preload; as=$preloadAs";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handlePreconnectFromNodeElement($nodeElement)
|
||||||
|
{
|
||||||
|
$url = $nodeElement->attributes->getNamedItem('href')->nodeValue;
|
||||||
|
|
||||||
|
$this->headers[] = "<$url>; rel=preconnect";
|
||||||
|
}
|
||||||
|
}
|
||||||
21
is_themecore/src/Hook/Smarty.php
Normal file
21
is_themecore/src/Hook/Smarty.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore\Hook;
|
||||||
|
|
||||||
|
class Smarty extends AbstractHook
|
||||||
|
{
|
||||||
|
public const HOOK_LIST = [
|
||||||
|
'actionDispatcherBefore',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function hookActionDispatcherBefore(): void
|
||||||
|
{
|
||||||
|
$this->context->smarty->registerPlugin('function', 'generateImagesSources', ['Oksydan\Module\IsThemeCore\Core\Smarty\SmartyHelperFunctions', 'generateImagesSources']);
|
||||||
|
$this->context->smarty->registerPlugin('function', 'generateImageSvgPlaceholder', ['Oksydan\Module\IsThemeCore\Core\Smarty\SmartyHelperFunctions', 'generateImageSvgPlaceholder']);
|
||||||
|
$this->context->smarty->registerPlugin('function', 'appendParamToUrl', ['Oksydan\Module\IsThemeCore\Core\Smarty\SmartyHelperFunctions', 'appendParamToUrl']);
|
||||||
|
$this->context->smarty->registerPlugin('block', 'images_block', ['Oksydan\Module\IsThemeCore\Core\Smarty\SmartyHelperFunctions', 'imagesBlock']);
|
||||||
|
$this->context->smarty->registerPlugin('block', 'cms_images_block', ['Oksydan\Module\IsThemeCore\Core\Smarty\SmartyHelperFunctions', 'cmsImagesBlock']);
|
||||||
|
$this->context->smarty->registerPlugin('block', 'display_mobile', ['Oksydan\Module\IsThemeCore\Core\Smarty\SmartyHelperFunctions', 'displayMobileBlock']);
|
||||||
|
$this->context->smarty->registerPlugin('block', 'display_desktop', ['Oksydan\Module\IsThemeCore\Core\Smarty\SmartyHelperFunctions', 'displayDesktopBlock']);
|
||||||
|
}
|
||||||
|
}
|
||||||
63
is_themecore/src/HookDispatcher.php
Normal file
63
is_themecore/src/HookDispatcher.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Oksydan\Module\IsThemeCore;
|
||||||
|
|
||||||
|
use Oksydan\Module\IsThemeCore\Hook\AbstractHook;
|
||||||
|
use Oksydan\Module\IsThemeCore\Hook\Assets;
|
||||||
|
use Oksydan\Module\IsThemeCore\Hook\Header;
|
||||||
|
use Oksydan\Module\IsThemeCore\Hook\Htaccess;
|
||||||
|
use Oksydan\Module\IsThemeCore\Hook\HtmlOutput;
|
||||||
|
use Oksydan\Module\IsThemeCore\Hook\Smarty;
|
||||||
|
|
||||||
|
class HookDispatcher
|
||||||
|
{
|
||||||
|
public const HOOK_CLASSES = [
|
||||||
|
Header::class,
|
||||||
|
Assets::class,
|
||||||
|
Smarty::class,
|
||||||
|
HtmlOutput::class,
|
||||||
|
Htaccess::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook instances.
|
||||||
|
*
|
||||||
|
* @var AbstractHook[]
|
||||||
|
*/
|
||||||
|
protected $hooks = [];
|
||||||
|
|
||||||
|
public function __construct(\Is_themecore $module)
|
||||||
|
{
|
||||||
|
foreach (static::HOOK_CLASSES as $hookClass) {
|
||||||
|
/** @var AbstractHook $hook */
|
||||||
|
$hook = new $hookClass($module);
|
||||||
|
$this->hooks[] = $hook;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get available hooks
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getAvailableHooks()
|
||||||
|
{
|
||||||
|
$availableHooks = [];
|
||||||
|
foreach ($this->hooks as $hook) {
|
||||||
|
$availableHooks = array_merge($availableHooks, $hook->getAvailableHooks());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $availableHooks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dispatch($hookName, array $params = [])
|
||||||
|
{
|
||||||
|
foreach ($this->hooks as $hook) {
|
||||||
|
if (method_exists($hook, $hookName)) {
|
||||||
|
return $hook->{$hookName}($params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
is_themecore/src/index.php
Normal file
11
is_themecore/src/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
4
is_themecore/tests/.env.dist
Normal file
4
is_themecore/tests/.env.dist
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
PHP71_EXEC_PATH="/usr/bin/php7.1"
|
||||||
|
PHP72_EXEC_PATH="/usr/bin/php7.2"
|
||||||
|
PHP73_EXEC_PATH="/usr/bin/php7.3"
|
||||||
|
PHP74_EXEC_PATH="/usr/bin/php7.4"
|
||||||
45
is_themecore/tests/grumphp.dist.yml
Normal file
45
is_themecore/tests/grumphp.dist.yml
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
grumphp:
|
||||||
|
tasks:
|
||||||
|
php71:
|
||||||
|
metadata:
|
||||||
|
task: shell
|
||||||
|
scripts:
|
||||||
|
- ["-c", "tests/php/php_syntax.sh $PHP71_EXEC_PATH"]
|
||||||
|
php72:
|
||||||
|
metadata:
|
||||||
|
task: shell
|
||||||
|
scripts:
|
||||||
|
- ["-c", "tests/php/php_syntax.sh $PHP72_EXEC_PATH"]
|
||||||
|
php73:
|
||||||
|
metadata:
|
||||||
|
task: shell
|
||||||
|
scripts:
|
||||||
|
- ["-c", "tests/php/php_syntax.sh $PHP73_EXEC_PATH"]
|
||||||
|
php74:
|
||||||
|
metadata:
|
||||||
|
task: shell
|
||||||
|
scripts:
|
||||||
|
- ["-c", "tests/php/php_syntax.sh $PHP74_EXEC_PATH"]
|
||||||
|
phpcsfixer:
|
||||||
|
config: tests/php/.php-cs-fixer.dist.php
|
||||||
|
phpstan:
|
||||||
|
configuration: tests/php/phpstan.dist.neon
|
||||||
|
eslint:
|
||||||
|
bin: _dev/node_modules/.bin/eslint
|
||||||
|
config: _dev/.eslintrc.js
|
||||||
|
header-stamp:
|
||||||
|
metadata:
|
||||||
|
task: shell
|
||||||
|
scripts:
|
||||||
|
- [
|
||||||
|
"-c",
|
||||||
|
'vendor/bin/header-stamp --dry-run
|
||||||
|
--extensions="php,js,css,scss,tpl,html.twig,vue" --exclude="vendor,node_modules" --license="_dev/header-stamp/afl.txt"',
|
||||||
|
]
|
||||||
|
fixer:
|
||||||
|
fix_by_default: true
|
||||||
|
environment:
|
||||||
|
files:
|
||||||
|
- tests/.env.dist
|
||||||
|
- tests/.env
|
||||||
|
|
||||||
14
is_themecore/tests/php/.php-cs-fixer.dist.php
Normal file
14
is_themecore/tests/php/.php-cs-fixer.dist.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
$rootDir = __DIR__ . '/../../';
|
||||||
|
|
||||||
|
$finder = PhpCsFixer\Finder::create()
|
||||||
|
->in($rootDir)
|
||||||
|
->exclude(['vendor', 'node_modules'])
|
||||||
|
;
|
||||||
|
|
||||||
|
$config = new PrestaShop\CodingStandards\CsFixer\Config();
|
||||||
|
return $config
|
||||||
|
->setUsingCache(true)
|
||||||
|
->setCacheFile(__DIR__.'/.php-cs-fixer.cache')
|
||||||
|
->setFinder($finder)
|
||||||
|
;
|
||||||
3
is_themecore/tests/php/.phpstan_bootstrap.neon
Normal file
3
is_themecore/tests/php/.phpstan_bootstrap.neon
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
parameters:
|
||||||
|
bootstrapFiles:
|
||||||
|
- %currentWorkingDirectory%/tests/php/.phpstan_bootstrap.php
|
||||||
70
is_themecore/tests/php/.phpstan_bootstrap.php
Normal file
70
is_themecore/tests/php/.phpstan_bootstrap.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
define('DEFAULT_PATH_TO_PRESTASHOP', '/path/to/prestashop/');
|
||||||
|
define('PHPSTAN_PS_ROOT_CONFIG_FILE', __DIR__ . '/.phpstan_bootstrap_config.php');
|
||||||
|
|
||||||
|
try {
|
||||||
|
if(!include_env_configuration()) {
|
||||||
|
create_default_config_file_and_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
abort_on_incorrect_configuration();
|
||||||
|
|
||||||
|
echo 'Phpstan configuration : _PS_ROOT_DIR_ successfully set to ' . getenv('_PS_ROOT_DIR_');
|
||||||
|
} catch (RuntimeException $exception) {
|
||||||
|
echo PHP_EOL . ' Phpstan configuration problem : ' . $exception->getMessage();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------- end of script -------------
|
||||||
|
|
||||||
|
function include_env_configuration(): bool
|
||||||
|
{
|
||||||
|
if (file_exists(PHPSTAN_PS_ROOT_CONFIG_FILE)) {
|
||||||
|
require PHPSTAN_PS_ROOT_CONFIG_FILE;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function create_default_config_file_and_exit(): void
|
||||||
|
{
|
||||||
|
if (!getenv('_PS_ROOT_DIR_')) {
|
||||||
|
echo 'Environment variable _PS_ROOT_DIR_ not set.' . PHP_EOL;
|
||||||
|
|
||||||
|
// check if config file exists and create a default one if needed.
|
||||||
|
if (!file_exists(PHPSTAN_PS_ROOT_CONFIG_FILE)) {
|
||||||
|
file_put_contents(PHPSTAN_PS_ROOT_CONFIG_FILE,
|
||||||
|
'<?php' . PHP_EOL .
|
||||||
|
'// replace ' . DEFAULT_PATH_TO_PRESTASHOP . ' with a local path.' . PHP_EOL .
|
||||||
|
'putenv(\'_PS_ROOT_DIR_=' . DEFAULT_PATH_TO_PRESTASHOP . '\');');
|
||||||
|
|
||||||
|
throw new RuntimeException(PHP_EOL . PHP_EOL . ' Missing phpstan configuration file.' . PHP_EOL . ' Default file created. Edit file "' . PHPSTAN_PS_ROOT_CONFIG_FILE . '"' . PHP_EOL . PHP_EOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException(PHP_EOL . ' Configuration file was found but _PS_ROOT_DIR_ is still not set.' . PHP_EOL . ' Edit or remove PHPSTAN_PS_ROOT_CONFIG_FILE to define a _PS_ROOT_DIR_ (using putenv()).');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function abort_on_incorrect_configuration(): void
|
||||||
|
{
|
||||||
|
// envirornnemt var not set
|
||||||
|
if (false === getenv('_PS_ROOT_DIR_')) {
|
||||||
|
throw new RuntimeException(PHP_EOL . PHP_EOL . ' _PS_ROOT_DIR_ not set on "' . PHPSTAN_PS_ROOT_CONFIG_FILE . PHP_EOL . ' Correct this file or delete it. ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// default path was left
|
||||||
|
if (DEFAULT_PATH_TO_PRESTASHOP === getenv('_PS_ROOT_DIR_')) {
|
||||||
|
throw new RuntimeException(PHP_EOL . PHP_EOL . ' You must configure the path to Prestashop in "' . PHPSTAN_PS_ROOT_CONFIG_FILE . PHP_EOL . ' You left the default value.' . PHP_EOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// abort if _PS_ROOT_DIR_ is not a valid path
|
||||||
|
if (!file_exists(getenv('_PS_ROOT_DIR_'))) {
|
||||||
|
throw new RuntimeException(PHP_EOL . PHP_EOL . ' Invalid path to prestashop. Provided : ' . getenv('_PS_ROOT_DIR_') . PHP_EOL . ' Edit "' . PHPSTAN_PS_ROOT_CONFIG_FILE . '" and modify _PS_ROOT_DIR_ ');
|
||||||
|
}
|
||||||
|
}
|
||||||
1
is_themecore/tests/php/php_syntax.sh
Normal file
1
is_themecore/tests/php/php_syntax.sh
Normal file
@ -0,0 +1 @@
|
|||||||
|
find . -type f -name '*.php' ! -path "./vendor/*" ! -path "./tests/*" -exec $1 -l -n {} \; 2>&1 | (! grep -v "No syntax errors detected")
|
||||||
14
is_themecore/tests/php/phpstan.dist.neon
Normal file
14
is_themecore/tests/php/phpstan.dist.neon
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
parameters:
|
||||||
|
paths:
|
||||||
|
# We consider that the extension file will be stored the folder test/phpstan
|
||||||
|
# From Phpstan 0.12, paths are relative to the .neon file.
|
||||||
|
- ../../src
|
||||||
|
- ../../is_themecore.php
|
||||||
|
ignoreErrors:
|
||||||
|
# - "#Insert your error here (don't forget to escape special characters like the following hash: \\#)#"
|
||||||
|
reportUnmatchedIgnoredErrors: true
|
||||||
|
level: 9
|
||||||
|
|
||||||
|
includes:
|
||||||
|
- %currentWorkingDirectory%/tests/php/.phpstan_bootstrap.neon
|
||||||
|
- %currentWorkingDirectory%/vendor/prestashop/php-dev-tools/phpstan/ps-module-extension.neon
|
||||||
11
is_themecore/translations/index.php
Normal file
11
is_themecore/translations/index.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||||
|
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||||
|
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
|
header('Location: ../');
|
||||||
|
exit;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user