Commit 29916927 authored by Tommy Meier's avatar Tommy Meier
Browse files

Add functionality to enable/display properties in listing

parent 479f39aa
PLUGIN_NAME="NetiNextToolKit"
#EXCLUDES=( "exclude dir" "exclude file" )
#PLUGINS_BUILD=( "npm-install" "npm-build" )
#PLUGINS_DIST=( "detect-license-check" )
#NPM_INSTALL=( "npm app dir" )
#NPM_BUILD=( "npm app dir" )
.idea
/.build/plugins/
/.build/tmp/
/.build/update/
# NetiNextToolKit
# ToolKit
>
* PluginKey: NetiNextToolKit
* ProjectId: [000000-012-832](https://redmine.netinventors.de/projects/000000-012-832)
## Requirements:
* Shopware version: 6.4.*
## Install:
1. Install Plugin via plugin manager or upload this plugin in "/custom/plugins/". The plugin directory should be named "NetiNextToolKit".
## Configuration:
* Install
* Configure
## Get involved
We highly appreciate if you want to add further functions and fix issues. Just fork our plugin and create a pull request.
For more information about contributing to this plugin, please see [CONTRIBUTING.md](CONTRIBUTING.md).
## License & Copyright
Copyright (c) 2020, Net Inventors - Agentur für digitale Medien GmbH
Please see [License file](LICENSE) for more information.
## Contact
**Net Inventors GmbH**
Agathe-Lasch-Weg 2
22605 Hamburg
Germany
T. 040 42934714-0 // F. 040 42934714-9
www.netinventors.de // info@netinventors.de
#!/bin/bash
SCRIPT_VERSION='1.0.0'
CURRENT_DIR=$(pwd)
WORKING_DIR=$(cd $(dirname $0) && pwd)
BUILD_DIR="${WORKING_DIR}/.build"
PLUGIN_DIR="${BUILD_DIR}/plugins"
TEMP_DIR="${BUILD_DIR}/tmp"
UPDATE=1
PLUGIN_NAME=""
PLUGINS_BUILD=()
PLUGINS_DIST=()
TARGET_DIR=${CURRENT_DIR}
usage() {
echo "Usage: $0 [-h] [-n] [-t <string>]" 1>&2;
exit 1;
}
while getopts "hnt:" opt; do
case "${opt}" in
h)
usage
;;
n)
UPDATE=0
;;
t)
TARGET_DIR=${OPTARG}
;;
*)
usage
;;
esac
done
shift $((OPTIND-1))
if [[ "/" != "${TARGET_DIR:0:1}" ]]; then
TARGET_DIR="${WORKING_DIR}/${TARGET_DIR}"
fi
if [[ ! -d ${TARGET_DIR} ]]; then
echo "ERROR: Target dir doesn't exist"
exit 2
fi
if [[ ! -w ${TARGET_DIR} ]]; then
echo "ERROR: Can't write to target dir"
exit 2
fi
TARGET_DIR=$(cd "${TARGET_DIR}" && pwd)
cleanup() {
echo "INFO: Cleanup ${1}"
rm -Rf ${1}
}
selfUpdate() {
echo "INFO: Run self update process"
UPDATE_DIR="${BUILD_DIR}/update"
git clone --quiet ssh://git@gitlab.netinventors.de:2202/shopware6/build-script.git ${UPDATE_DIR}
cp -Rp ${UPDATE_DIR}/.build/plugins ${WORKING_DIR}/.build
cp -p ${UPDATE_DIR}/build.sh ${WORKING_DIR}
cleanup ${UPDATE_DIR}
}
runPlugins() {
if [[ -z ${@} ]]; then
return 0
fi
for PLUGIN in "${@}"; do
EXECUTABLE="${PLUGIN_DIR}/${PLUGIN}.sh"
if [[ -r "${EXECUTABLE}" ]]; then
echo "INFO: Run plugin ${PLUGIN}"
source "${EXECUTABLE}"
__FN="__$(sed -r 's/(\-)([a-z])/\U\2/g' <<< "${PLUGIN}")"
${__FN}
__RETURN=$?
if [[ 2 -eq ${__RETURN} ]]; then
echo "ERROR: A plugin stops the build process"
cleanup ${TEMP_DIR}
exit 2
fi
else
echo "WARNING: Plugin executable ${EXECUTABLE} not found"
fi
done
}
build() {
mkdir -p ${BUILD_DIR}
if [[ 1 -eq ${UPDATE} ]]; then
selfUpdate
bash -c "$0 -n -t \"${TARGET_DIR}\""
exit $?
fi
echo "INFO: Start build script version ${SCRIPT_VERSION}"
if [[ ! -r "${BUILD_DIR}/build.cfg" ]]; then
echo "ERROR: Unable to load build configuration file ${BUILD_DIR}/build.cfg"
exit 2
fi
# Load build configuration file
source "${BUILD_DIR}/build.cfg"
if [[ -z "${PLUGIN_NAME}" ]]; then
echo 'ERROR: Unable to resolve plugin name'
exit 2
fi
if [[ ! -r "${WORKING_DIR}/composer.json" ]]; then
echo "ERROR: Plugin describing file ${WORKING_DIR}/composer.json not found"
exit 2
fi
DIST_DIR="${BUILD_DIR}/tmp/${PLUGIN_NAME}"
VERSION=$(php -r "echo json_decode(file_get_contents('${WORKING_DIR}/composer.json'), true)['version'];")
EXCLUDES=( ".build" ".idea" "build.sh" ".git" ".gitignore" "${EXCLUDES[@]}" )
ZIP_FILE="${TARGET_DIR}/${PLUGIN_NAME}-${VERSION}.zip"
if [[ -z "$VERSION" ]]; then
echo 'ERROR: Unable to resolve plugin version from composer.json'
exit 2
fi
# Remove existing package file
if [[ -w "${ZIP_FILE}" ]]; then
rm "${ZIP_FILE}"
fi
if [[ -f "${ZIP_FILE}" ]]; then
echo "ERROR: Cannot override existing build file ${ZIP_FILE}"
exit 2
fi
RSYNC_EXCLUDES=( --exclude="${PLUGIN_NAME}-*.zip" )
cd ${WORKING_DIR}
runPlugins "${PLUGINS_BUILD[@]}"
for EXCLUDE in "${EXCLUDES[@]}"; do
RSYNC_EXCLUDES+=( --exclude="${EXCLUDE}" )
done
# Remove old temp dir
if [[ -d ${TEMP_DIR} ]]; then
rm -Rf ${TEMP_DIR}
fi
# Create temporary build folder
mkdir -p ${DIST_DIR}
# Copy all files from source folder to temporary folder
rsync -avzq "${RSYNC_EXCLUDES[@]}" . ${DIST_DIR}
# Step into the temporary build folder
cd ${DIST_DIR}
runPlugins "${PLUGINS_DIST[@]}"
if [ $(uname -s) == "Linux" ]; then
# Replace __SECRET__ in plugin bootstrap to detect manipulations
SECRET=$(head -c 1000 /dev/urandom | tr -dc 'A-Za-z0-9%,!_;:#@' | fold -w 32 | head -n 1)
sed -i "s/__SECRET__/${SECRET}/g" "src/${PLUGIN_NAME}.php"
# Create md5.json file including the md5 checksum of every file in the package and the plugin __SECRET__
echo "<?php return [" > md5checksum.php
for FILE in $(find -type f -printf '%P\n'); do
echo "'${FILE}' => '$(md5sum ${FILE} | awk '{ print $1 }')'," >> md5checksum.php
done;
echo "'__SECRET__' => '${SECRET}'," >> md5checksum.php
echo "];" >> md5checksum.php
fi
cd ${TEMP_DIR}
# Zip temporary folder contents to package file in plugin folder
zip -qr "${ZIP_FILE}" $(basename ${DIST_DIR})
echo "INFO: Created build package is located in ${ZIP_FILE}"
cleanup ${TEMP_DIR}
cd ${CURRENT_DIR}
}
build
{
"name": "netinventors/next-tool-kit",
"description": "",
"version": "4.0.0",
"type": "shopware-platform-plugin",
"license": "proprietary",
"authors": [
{
"name": "Net Inventors GmbH"
}
],
"require": {
"shopware/core": "6.4.*",
"shopware/storefront": "6.4.*"
},
"minimum-stability": "RC",
"extra": {
"shopware-plugin-class": "NetInventors\\NetiNextToolKit\\NetiNextToolKit",
"plugin-icon": "src/Resources/plugin.png",
"label": {
"de-DE": "ToolKit",
"en-GB": "ToolKit"
},
"description": {
"de-DE": "",
"en-GB": ""
},
"manufacturerLink": {
"de-DE": "https://www.netinventors.de/",
"en-GB": "https://www.netinventors.de/"
}
},
"autoload": {
"psr-4": {
"NetInventors\\NetiNextToolKit\\": "src/"
}
}
}
<?php
declare(strict_types=1);
namespace NetInventors\NetiNextToolKit\Components;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use NetInventors\NetiNextToolKit\Core\Content\PropertyGroup\Aggregate\Attribute\AttributeDefinition;
use Shopware\Core\Framework\Plugin\Context\InstallContext;
use Symfony\Component\DependencyInjection\ContainerInterface;
class Setup
{
/**
* @var InstallContext
*/
protected $context;
/**
* @var ContainerInterface
*/
protected $container;
/**
* Setup constructor.
*
* @param InstallContext $context
* @param ContainerInterface $container
*/
public function __construct(InstallContext $context, ContainerInterface $container)
{
$this->context = $context;
$this->container = $container;
}
/**
* @throws \Throwable
*/
public function install(): void
{
}
/**
* @throws DBALException
*/
public function uninstall(): void
{
$this->deleteTables();
}
/**
* @throws DBALException
*/
protected function deleteTables(): void
{
/** @var Connection $connection */
$connection = $this->container->get(Connection::class);
$connection->executeStatement('SET foreign_key_checks=0;');
$connection->executeStatement('DROP TABLE IF EXISTS ' . AttributeDefinition::ENTITY_NAME);
$connection->executeStatement('SET foreign_key_checks=1;');
}
}
<?php
declare(strict_types=1);
namespace NetInventors\NetiNextToolKit\Core\Content\PropertyGroup\Aggregate\Attribute;
use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
class AttributeCollection extends EntityCollection
{
protected function getExpectedClass(): string
{
return AttributeEntity::class;
}
}
\ No newline at end of file
<?php
declare(strict_types=1);
namespace NetInventors\NetiNextToolKit\Core\Content\PropertyGroup\Aggregate\Attribute;
use Shopware\Core\Content\Property\PropertyGroupDefinition;
use Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition;
use Shopware\Core\Framework\DataAbstractionLayer\Field\BoolField;
use Shopware\Core\Framework\DataAbstractionLayer\Field\FkField;
use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\PrimaryKey;
use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\Required;
use Shopware\Core\Framework\DataAbstractionLayer\Field\IdField;
use Shopware\Core\Framework\DataAbstractionLayer\Field\ManyToOneAssociationField;
use Shopware\Core\Framework\DataAbstractionLayer\FieldCollection;
class AttributeDefinition extends EntityDefinition
{
public const ENTITY_NAME = 'neti_tk_property_group_attribute';
public function getEntityName(): string
{
return self::ENTITY_NAME;
}
public function getEntityClass(): string
{
return AttributeEntity::class;
}
public function getCollectionClass(): string
{
return AttributeCollection::class;
}
protected function defineFields(): FieldCollection
{
return new FieldCollection([
(new IdField('id', 'id'))->addFlags(new Required(), new PrimaryKey()),
(new FkField('property_group_id', 'propertyGroupId', PropertyGroupDefinition::class))->addFlags(new Required()),
(new ManyToOneAssociationField('propertyGroup', 'property_group_id', PropertyGroupDefinition::class)),
new BoolField('display_in_listing', 'displayInListing'),
]);
}
}
\ No newline at end of file
<?php
declare(strict_types=1);
namespace NetInventors\NetiNextToolKit\Core\Content\PropertyGroup\Aggregate\Attribute;
use Shopware\Core\Content\Property\PropertyGroupEntity;
use Shopware\Core\Framework\DataAbstractionLayer\Entity;
use Shopware\Core\Framework\DataAbstractionLayer\EntityIdTrait;
class AttributeEntity extends Entity
{
use EntityIdTrait;
protected string $propertyGroupId;
protected ?PropertyGroupEntity $propertyGroup;
protected bool $displayInListing;
public function getPropertyGroupId(): string
{
return $this->propertyGroupId;
}
public function setPropertyGroupId(string $propertyGroupId): void
{
$this->propertyGroupId = $propertyGroupId;
}
public function getPropertyGroup(): ?PropertyGroupEntity
{
return $this->propertyGroup;
}
public function setPropertyGroup(?PropertyGroupEntity $propertyGroup): void
{
$this->propertyGroup = $propertyGroup;
}
public function isDisplayInListing(): bool
{
return $this->displayInListing;
}
public function setDisplayInListing(bool $displayInListing): void
{
$this->displayInListing = $displayInListing;
}
}
\ No newline at end of file
<?php
declare(strict_types=1);
namespace NetInventors\NetiNextToolKit\Core\Content\PropertyGroup;
use NetInventors\NetiNextToolKit\Core\Content\PropertyGroup\Aggregate\Attribute\AttributeDefinition;
use Shopware\Core\Content\Property\PropertyGroupDefinition;
use Shopware\Core\Framework\DataAbstractionLayer\EntityExtension;
use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\CascadeDelete;
use Shopware\Core\Framework\DataAbstractionLayer\Field\OneToOneAssociationField;
use Shopware\Core\Framework\DataAbstractionLayer\FieldCollection;
class PropertyGroupExtension extends EntityExtension
{
public function getDefinitionClass(): string
{
return PropertyGroupDefinition::class;
}
public function extendFields(FieldCollection $collection): void
{
$collection->add(
(new OneToOneAssociationField(
'netiToolKitAttribute',
'id',
'property_group_id',
AttributeDefinition::class
))->addFlags(new CascadeDelete())
);
}
}
\ No newline at end of file
<?php
declare(strict_types=1);
namespace NetInventors\NetiNextToolKit\Decorator;
use Shopware\Core\Content\Product\SalesChannel\Listing\AbstractProductListingRoute;
use Shopware\Core\Content\Product\SalesChannel\Listing\ProductListingRouteResponse;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\HttpFoundation\Request;
class ProductListingRouteDecorator extends AbstractProductListingRoute
{
private AbstractProductListingRoute $mainService;
public function __construct(
AbstractProductListingRoute $mainService
) {
$this->mainService = $mainService;
}
public function getDecorated(): AbstractProductListingRoute
{
return $this->mainService;
}
public function load(
string $categoryId,
Request $request,
SalesChannelContext $context,
Criteria $criteria
): ProductListingRouteResponse {
$criteria->addAssociation('properties');
$criteria->getAssociation('properties')->addFilter(
new EqualsFilter('group.netiToolKitAttribute.displayInListing', true)
);
return $this->mainService->load($categoryId, $request, $context, $criteria);
}
}
\ No newline at end of file
<?php
declare(strict_types=1);
namespace NetInventors\NetiNextToolKit\Migration;
use Doctrine\DBAL\Connection;
use Shopware\Core\Framework\Migration\MigrationStep;
class Migration1634557439 extends MigrationStep
{
public function getCreationTimestamp(): int
{
return 1634557439;
}
public function update(Connection $connection): void
{
$sql = '
CREATE TABLE `neti_tk_property_group_attribute` (
`id` BINARY(16) NOT NULL,
`property_group_id` BINARY(16),
`display_in_listing` TINYINT(1) NULL DEFAULT \'0\',
`created_at` DATETIME(3) NOT NULL,
`updated_at` DATETIME(3) NULL,
PRIMARY KEY (`id`),
KEY `fk.neti_tk_property_group_attribute.property_group_id` (`property_group_id`),
CONSTRAINT `fk.neti_tk_property_group_attribute.property_group_id` FOREIGN KEY (`property_group_id`) REFERENCES `property_group` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
';
$connection->executeStatement($sql);
}
public function updateDestructive(Connection $connection): void
{
}
}
\ No newline at end of file
<?php
declare(strict_types=1);
namespace NetInventors\NetiNextToolKit;
use Doctrine\DBAL\DBALException;
use Shopware\Core\Framework\Plugin;
use NetInventors\NetiNextToolKit\Components\Setup;
use Shopware\Core\Framework\Plugin\Context\InstallContext;
use Shopware\Core\Framework\Plugin\Context\UninstallContext;
class NetiNextToolKit extends Plugin
{
/**
* @param InstallContext $installContext
*
* @throws \Throwable
*/