Initial commit: is_shoppingcart out of the box v3.0.1
v
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