Skip to content
This repository has been archived by the owner on Jun 7, 2021. It is now read-only.

Commit

Permalink
Merge branch 'master' of github.com:heimrichhannot/contao-isotope-bundle
Browse files Browse the repository at this point in the history
  • Loading branch information
Konstantin Wagner committed Jun 12, 2018
2 parents 710b161 + 9d2b6d2 commit c27fb66
Show file tree
Hide file tree
Showing 20 changed files with 565 additions and 46 deletions.
18 changes: 13 additions & 5 deletions CHANGELOG.md
@@ -1,7 +1,15 @@
# Changelog
All notable changes to this project will be documented in this file.

## [0.9.3] -2018-06-05
## [0.10.0] - 2018-06-08

#### Added
* Backend Booking information for product

#### Changed
* better usage of encore bundle for frontend

## [0.9.3] - 2018-06-05

#### Fixed
* error added items with no booking information to cart
Expand All @@ -12,13 +20,13 @@ All notable changes to this project will be documented in this file.
* empty pids for booking items
* booking selection not mandatory

## [0.9.1] -2018-06-04
## [0.9.1] - 2018-06-04

#### Fixed
* error on save (removed sync on Model save)
* return wrong product model type from model methods (added ProductModel as "standard" product isotope type)

## [0.9.0] -2018-06-04
## [0.9.0] - 2018-06-04

#### Added
* sync method for ProductModel and ProductDataModel
Expand All @@ -33,12 +41,12 @@ All notable changes to this project will be documented in this file.
You need to call the database updater!
You should call the upgrade command!

## [0.8.1] -2018-06-01
## [0.8.1] - 2018-06-01

#### Fixed
* error adding a item with no booking functionality to cart

## [0.8.0] -2018-06-01
## [0.8.0] - 2018-06-01

#### Added
* validation for product booking an add to cart and checkout
Expand Down
25 changes: 21 additions & 4 deletions README.md
Expand Up @@ -25,15 +25,34 @@ composer require heimrichhannot/contao-isotope-bundle

You need to call the database update tool after install.

If you update from an older version as 0.8 or from isotope_plus module, you need to call the upgrade command (see usage).
If you update from an older version as 0.8 or from isotope_plus module, you need to call the upgrade command (see [Developers](#developers)).

We recommend use [Contao Encore](https://github.com/heimrichhannot/contao-encore-bundle) for assets managment.

## Usage

#### Booking

<img src="docs/img/booking_frontend.png" width="250">

To add booking functionality to a product, you need to active the booking action in the frontend module for bookable products.

In the backend, you can add the fields bookingOverview, bookingReservedDates and bookingOverview.

Field | Description
------|-----------
bookingOverview | Days to block around booking date (for shipping, printing, ...)
bookingReservedDates | Reserve date to block booking
bookingOverview | Displays a calendar containing informations about booked items.




## Developers

### Upgrade from older version or module

If you upgrade from [Isotope_Plus](https://github.com/heimrichhannot/contao-isotope_plus) or the bundle prior to version 0.7, you should use the upgrade command to setup the product data table.
If you upgrade from [Isotope_Plus](https://github.com/heimrichhannot/contao-isotope_plus) or the bundle prior to version 0.8, you should use the upgrade command to setup the product data table.

```php
# From project folder:
Expand All @@ -42,8 +61,6 @@ php vendor/bin/contao-console huh:isotope:upgrade

This command fills the product data table based on the data stored in product table.

## Developers

### Commands

For more informations use the help function (`[command] --help`).
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Expand Up @@ -9,7 +9,7 @@
"isotope/isotope-core": "*",
"symfony/framework-bundle": "^3.4",
"heimrichhannot/contao-utils-bundle": "^2.0",
"heimrichhannot/contao-fieldpalette": ">=1.2.9",
"heimrichhannot/contao-fieldpalette-bundle": "^0.2",
"heimrichhannot/contao-frontendedit": "^6.0",
"contao-components/tablesorter": ">=2.0.5.2",
"heimrichhannot/contao-request-bundle": "^1.0",
Expand Down
Binary file added docs/img/booking_frontend.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 13 additions & 3 deletions src/Action/BookingPlanAction.php
Expand Up @@ -19,6 +19,13 @@

class BookingPlanAction extends CartAction
{
protected $actionManager;

public function __construct()
{
$this->actionManager = System::getContainer()->get('huh.ajax.action');
}

public function getName()
{
return 'edit_booking_plan';
Expand All @@ -36,12 +43,15 @@ public function getBlockedDates($product)

public function generate(IsotopeProduct $product, array $config = [])
{
$url = $this->actionManager->generateUrl(AjaxManager::ISOTOPE_AJAX_GROUP, AjaxManager::ISOTOPE_AJAX_BOOKING_PLAN_UPDATE);

return sprintf(
'<div class="bookingPlan_container" data-update="%s" data-product-id="%s">
<label for="bookingPlan">%s</label>
<input type="text" name="%s" id="bookingPlan" class="submit %s %s" data-blocked="%s" required></div>',
System::getContainer()->get('huh.ajax.action')->generateUrl(AjaxManager::ISOTOPE_AJAX_GROUP, AjaxManager::ISOTOPE_AJAX_BOOKING_PLAN_UPDATE),
<label for="%s">%s</label>
<input type="text" name="%s" id="bookingPlan" class="submit %s %s" data-blocked="%s" required></div>',
$url,
$product->id,
$this->getName(),
$this->getLabel(),
$this->getName(),
$this->getName(),
Expand Down
107 changes: 107 additions & 0 deletions src/Attribute/BookingAttributes.php
Expand Up @@ -14,8 +14,10 @@
use HeimrichHannot\IsotopeBundle\Manager\IsotopeManager;
use HeimrichHannot\IsotopeBundle\Manager\ProductDataManager;
use HeimrichHannot\IsotopeBundle\Model\ProductCollectionItemModel;
use HeimrichHannot\IsotopeBundle\Model\ProductModel;
use HeimrichHannot\UtilsBundle\Model\ModelUtil;
use Isotope\Interfaces\IsotopeProduct;
use Isotope\Model\ProductCollection;
use Isotope\Model\ProductCollectionItem;
use Symfony\Component\Translation\TranslatorInterface;

Expand Down Expand Up @@ -114,6 +116,83 @@ public function getBlockedDates($product, int $quantity = 1)
return $this->getBlockedDatesByItems($collectionItems, $product, $quantity);
}

/**
* Returns a list of orders for given products for requested day.
*
* @param ProductModel $product
* @param int $day
* @param int $month
* @param int $year
*
* @return Collection|array|ProductCollection[]|null
*/
public function getOrdersWithBookingsByDay(ProductModel $product, int $day, int $month, int $year)
{
$orders = [];
$start = mktime(0, 0, 0, $month, $day, $year);
$end = mktime(23, 59, 59, $month, $day, $year);
$items = $this->getBookedItemsInTimeRange($product, $start, $end, true);
if (!$items) {
return $orders;
}
foreach ($items as $item) {
$orders[$item->pid]['items'][] = $item;
$orders[$item->pid]['order'] = ProductCollection::findOneBy(['id =?', 'type=?'], [$item->pid, 'order']);
}

return $orders;
}

/**
* Return a list with number of bookings per day.
*
* Includes reservations an blocked days.
*
* @param ProductModel $product
* @param int $month
* @param int $year
*
* @return array
*/
public function getBookingCountsByMonth(ProductModel $product, int $month, int $year)
{
$firstDay = mktime(0, 0, 0, $month, 1, $year);
$monthDays = date('t', mktime(0, 0, 0, $month, 1, $year));
$lastDay = mktime(23, 59, 59, $month, $monthDays, $year);

$bookingList = [];
$bookingList['booked'] = $bookingList['blocked'] = $bookingList['reserved'] = array_fill(1, $monthDays, 0);
$items = $this->getBookedItemsInTimeRange($product, $firstDay, $lastDay);
if (!$items) {
return $bookingList;
}
foreach ($items as $item) {
$range = $this->getRange($item->bookingStart, $item->bookingStop, $product->bookingBlock ?: 0);
$startDay = date('j', $item->bookingStart);
$endDay = date('j', $item->bookingStop);
foreach ($range as $tstamp) {
if ($year == date('Y', $tstamp) && ($month == date('n', $tstamp))) {
$selectedDay = date('j', $tstamp);
if ($selectedDay < $startDay || $selectedDay > $endDay) {
++$bookingList['blocked'][$selectedDay];
continue;
}
++$bookingList['booked'][$selectedDay];
}
}
}
$reservedDates = $this->getReservedDates($product);
foreach ($reservedDates as $reserved) {
foreach ($reserved as $tstamp) {
if ($year == date('Y', $tstamp) && ($month == date('n', $tstamp))) {
++$bookingList['reserved'][date('j', $tstamp)];
}
}
}

return $bookingList;
}

/**
* @param Collection $collectionItems
* @param IsotopeProduct $product
Expand Down Expand Up @@ -172,6 +251,34 @@ public function splitUpBookingDates(string $booking)
return [strtotime(trim($bookingDates[0])), strtotime(trim($bookingDates[1]))];
}

/**
* @param ProductModel $product
* @param int $startDate
* @param int $endDate
* @param bool $ignoreBlocking
*
* @return Collection|ProductCollectionItemModel[]|null
*/
protected function getBookedItemsInTimeRange(ProductModel $product, int $startDate, int $endDate, bool $ignoreBlocking = false)
{
$searchRange = 0;
if ($product->bookingBlock && !$ignoreBlocking) {
//search block range * 2 to get also overlapping block dates and add 1 day to get the booking date
$searchRange = (86400 * $product->bookingBlock * 2) + 1;
}
$firstDayWithBlocking = $startDate - $searchRange;
$lastDayWithBlocking = $endDate + $searchRange;

return ProductCollectionItemModel::findBy([
'product_id = ?',
"((bookingStart <= $lastDayWithBlocking AND bookingStop >= $startDate) ".
"OR (bookingStart <= $endDate AND bookingStop >= $firstDayWithBlocking) ".
"OR (bookingStart <= $startDate AND bookingStop >= $endDate))",
], [
(int) $product->id,
]);
}

/**
* get the booking dates for a product from collectionItems.
*
Expand Down
22 changes: 21 additions & 1 deletion src/ContaoManager/Plugin.php
Expand Up @@ -14,12 +14,16 @@
use Contao\ManagerPlugin\Bundle\Parser\ParserInterface;
use Contao\ManagerPlugin\Config\ContainerBuilder;
use Contao\ManagerPlugin\Config\ExtensionPluginInterface;
use Contao\ManagerPlugin\Routing\RoutingPluginInterface;
use HeimrichHannot\EncoreBundle\HeimrichHannotContaoEncoreBundle;
use HeimrichHannot\IsotopeBundle\HeimrichHannotContaoIsotopeBundle;
use HeimrichHannot\SlickBundle\HeimrichHannotContaoSlickBundle;
use HeimrichHannot\UtilsBundle\Container\ContainerUtil;
use Symfony\Component\Config\Loader\LoaderResolverInterface;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\RouteCollection;

class Plugin implements BundlePluginInterface, ExtensionPluginInterface
class Plugin implements BundlePluginInterface, ExtensionPluginInterface, RoutingPluginInterface
{
/**
* {@inheritdoc}
Expand Down Expand Up @@ -54,4 +58,20 @@ public function getExtensionConfig($extensionName, array $extensionConfigs, Cont
return ContainerUtil::mergeConfigFile('huh_encore', $extensionName, $extensionConfigs,
$container->getParameter('kernel.project_dir').'/vendor/heimrichhannot/contao-isotope-bundle/src/Resources/config/config_encore.yml');
}

/**
* Returns a collection of routes for this bundle.
*
* @param LoaderResolverInterface $resolver
* @param KernelInterface $kernel
*
* @return RouteCollection|null
*/
public function getRouteCollection(LoaderResolverInterface $resolver, KernelInterface $kernel)
{
return $resolver
->resolve(__DIR__.'/../Resources/config/routing.yml')
->load(__DIR__.'/../Resources/config/routing.yml')
;
}
}
91 changes: 91 additions & 0 deletions src/Controller/BackendController.php
@@ -0,0 +1,91 @@
<?php

/*
* Copyright (c) 2018 Heimrich & Hannot GmbH
*
* @license LGPL-3.0-or-later
*/

namespace HeimrichHannot\IsotopeBundle\Controller;

use Contao\CoreBundle\Framework\ContaoFrameworkInterface;
use HeimrichHannot\IsotopeBundle\Attribute\BookingAttributes;
use HeimrichHannot\IsotopeBundle\Model\ProductModel;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Translation\TranslatorInterface;

/**
* @Route("/contao/isotope_bundle", defaults={
* "_scope" = "backend",
* "_token_check" = true
* })
*/
class BackendController extends AbstractController
{
const ROUTE = '/contao/isotope_bundle';

/**
* @Route("/bookinglist", name="huh.isotope.backend.bookinglist")
* @Template("@HeimrichHannotContaoIsotope/backend/bookinglist.html.twig")
*
* @param Request $request
* @param ContaoFrameworkInterface $contaoFramework
* @param BookingAttributes $bookingAttributes
* @param TranslatorInterface $translator
*
* @return array
*/
public function bookingListAction(Request $request, ContaoFrameworkInterface $contaoFramework, BookingAttributes $bookingAttributes, TranslatorInterface $translator)
{
if (!$contaoFramework->isInitialized()) {
$contaoFramework->initialize();
}
$id = $request->get('id');
if (!is_numeric($id) | !$product = ProductModel::findById($id)) {
return ['error' => $translator->trans('Invalid id')];
}
$day = is_numeric($request->get('day')) ? (int) $request->get('day') : date('d');
$month = is_numeric($request->get('month')) ? (int) $request->get('month') : date('n');
$year = is_numeric($request->get('year')) ? (int) $request->get('year') : date('Y');
$orders = $bookingAttributes->getOrdersWithBookingsByDay($product, $day, $month, $year);
$date = mktime(0, 0, 0, $month, $day, $year);

return [
'product' => $product,
'orders' => $orders,
'tstamp' => $date,
];
}

/**
* @Route("/bookingoverview", name="huh.isotope.backend.bookingoverview")
* @Template("@HeimrichHannotContaoIsotope/attribute/bookingoverview.html.twig")
*
* @param Request $request
* @param ContaoFrameworkInterface $contaoFramework
* @param BookingAttributes $bookingAttributes
* @param TranslatorInterface $translator
*
* @return array
*/
public function bookingOverviewAction(Request $request, ContaoFrameworkInterface $contaoFramework, BookingAttributes $bookingAttributes, TranslatorInterface $translator)
{
$id = $request->get('id');
if (!is_numeric($id) | !$product = ProductModel::findById($id)) {
return ['error' => $translator->trans('Invalid id')];
}
$bookings = $bookingAttributes->getBookingCountsByMonth($product, date('n'), date('Y'));
$year = is_numeric($request->get('year')) ? (int) $request->get('year') : date('Y');
$month = is_numeric($request->get('month')) ? (int) $request->get('month') : date('n');
$date = mktime(0, 0, 0, $month, 1, $year);

return [
'bookings' => $bookings,
'product' => $product,
'time' => $date,
];
}
}

0 comments on commit c27fb66

Please sign in to comment.