Compare commits

..

42 commits

Author SHA1 Message Date
d70029951f Merge pull request 'Update command' (#28) from develop into master
Reviewed-on: thisfro/pflaenz.li#28
2023-02-08 17:25:32 +01:00
Jannis Portmann
80ef8caf20 Update command 2023-02-08 17:23:24 +01:00
d2612882c8 Merge pull request 'Use deployer 7.x' (#27) from develop into master
Reviewed-on: thisfro/pflaenz.li#27
2023-02-08 17:13:35 +01:00
cc5e0b6b27 Merge branch 'master' into develop 2023-02-08 17:13:22 +01:00
Jannis Portmann
a6e4b205f7 Use deployer 7.x 2023-02-08 17:11:54 +01:00
4e5b69450e Merge pull request 'Update and fix security issues' (#26) from develop into master
Reviewed-on: thisfro/pflaenz.li#26
2023-02-03 15:43:06 +01:00
Jannis Portmann
a543fd2a4a Update and fix security issues 2023-02-03 15:37:46 +01:00
e70157067c Merge pull request 'Update packages' (#25) from develop into master
Reviewed-on: thisfro/pflaenz.li#25
2022-09-02 16:20:31 +02:00
2d31f7cd76 Update packages 2022-09-02 16:09:07 +02:00
1c172ec454 Ignore snyk files 2022-09-02 16:08:48 +02:00
90d6fd0770 Set headers on all requests 2022-01-26 17:13:00 +01:00
5eeb662d6f Add simple favicon 2022-01-26 17:12:34 +01:00
220d6a1f83 Add icon for distance 2022-01-26 12:50:08 +01:00
92c07b02cb Fix password resetting 2022-01-26 12:47:48 +01:00
48bf4f26c4 Add icons for filterForm 2022-01-26 12:37:20 +01:00
c7b400cec3 Add padding 2022-01-26 12:37:07 +01:00
f86319f23c Remove umlaut from url 2022-01-26 10:10:54 +01:00
7b9542f0bd Redirect to offer after editing 2022-01-26 10:10:19 +01:00
3d0ffa3311 Always load app.js and matomo script 2022-01-25 18:32:34 +01:00
42564e085c Split app.js into individual scripts 2022-01-25 18:27:51 +01:00
304c0d4ba6 Always use https for tracking 2022-01-25 17:38:27 +01:00
f87b3a0115 Fix keywords 2022-01-25 16:27:30 +01:00
e327966ef0 Fix typo 2022-01-24 23:57:36 +01:00
0c5f93bc36 Change indention 2022-01-24 23:52:29 +01:00
54fa878115 Remove staging host 2022-01-24 23:49:31 +01:00
34e5132c3d Update readme 2022-01-24 23:49:23 +01:00
61b861b510 Add staging 2022-01-24 17:40:19 +01:00
a034a6118a Use domain without umlaut 2022-01-24 17:31:46 +01:00
3135c26345 Add link to terms 2022-01-24 15:34:35 +01:00
af12833642 Snyk test composer and npm 2022-01-24 10:31:21 +01:00
1f140475b9 Remove again 2022-01-24 10:14:01 +01:00
d4a7bd4228 Use correct name 2022-01-24 10:13:01 +01:00
f0c2bfae3a Add token from env 2022-01-24 10:05:00 +01:00
66e5631c16 Use correct snyk installation 2022-01-23 12:08:23 +01:00
ee36881c49 Remove --no-dev 2022-01-23 12:06:24 +01:00
e4972d1b32 Also run as sh 2022-01-23 10:56:28 +01:00
1296ca7cfb Fix typo 2022-01-23 10:54:52 +01:00
6b1406fde9 Run as sh 2022-01-23 00:42:48 +01:00
775dcfa681 Merge branch 'master' of ssh://git.thisfro.ch:222/thisfro/pflaenz.li 2022-01-23 00:38:47 +01:00
b47e2c1b14 New pipeline 2022-01-23 00:37:00 +01:00
93eef67209 Update packages 2022-01-20 17:51:38 +01:00
865f8ec94f Implement basic title text search 2022-01-20 17:51:02 +01:00
27 changed files with 4341 additions and 4625 deletions

3
.gitignore vendored
View file

@ -243,3 +243,6 @@ temp/
# .pnp.* # .pnp.*
# End of https://www.toptal.com/developers/gitignore/api/node,phpunit,symfony,composer,yarn # End of https://www.toptal.com/developers/gitignore/api/node,phpunit,symfony,composer,yarn
# Snyk
.dccache

45
Jenkinsfile vendored
View file

@ -2,36 +2,39 @@ node {
def app def app
stage('Clone repository') { stage('Clone repository') {
/* Let's make sure we have the repository cloned to our workspace */ // Let's make sure we have the repository cloned to our workspace
checkout scm checkout scm
} }
stage('Build image') { stage('Install dependencies') {
/* This builds the actual image; synonymous to // Install dependencies for build later
* docker build on the command line */ sh 'composer update'
sh 'yarn install'
app = docker.build("thisfro/plantex")
} }
stage('Test image') { stage('Composer Vulnr test') {
/* Ideally, we would run a test framework against our image. snykSecurity(
* For this example, we're using a Volkswagen-type approach ;-) */ snykInstallation: 'snyk-local',
targetFile: 'composer.lock',
app.inside { )
// php 'bin/phpunit'
sh 'echo "success"'
}
} }
stage('Push image') { stage('npm vulnr test') {
docker.withRegistry('https://hub.thisfro.ch') { snykSecurity(
app.push("$BUILD_NUMBER") snykInstallation: 'snyk-local',
app.push('latest') targetFile: 'package.json',
} )
} }
stage('Deploy staging') { stage('Deploy staging') {
sh 'cd /opt/containers/pflaenz.li && docker-compose pull && docker-compose up -d' // Deploy to staging host
sh 'vendor/bin/dep deploy lq5xi.ftp.infomaniak.com --no-interaction'
} }
/*
stage('Test staging') {
// Run phpunit tests on staging host
bin/phpunit COMMAND
}
*/
} }

View file

@ -1,11 +1,14 @@
# plant-exchange # Pflänz.li
## Idea ## Idea
A platform where people can exchange plants. They can post what they have and search for others with [filters](#filters). A platform where people can trade their plants. You can post what you have and search for others with [filters](#filters). The aim is to make it easier to trade plants and collect as few data as possible. Only the email/username and a postal code is required.
## Tech stack ## Tech stack
- [Symfony](https://symfony.com/) - [Symfony](https://symfony.com/)
- [MariaDB](https://www.mariadb.org) - [MariaDB](https://www.mariadb.org)
- [Deployer](https://deployer.org)
Can easily be depoyed to a LAMP server
## Admin dashboard ## Admin dashboard
Find it under `/admin` Find it under `/admin`
@ -14,8 +17,11 @@ Find it under `/admin`
### Implemented ### Implemented
- Distance between postal codes - Distance between postal codes
- Search within title
### Ideas ### Ideas
It would be nice to have categories somehow, but it would be hard to make it comprehensive.
:warning: This list is work in progress! :warning: This list is work in progress!
Searching with filters such as: Searching with filters such as:

View file

@ -8,7 +8,6 @@
// any CSS you import will output into a single css file (app.css in this case) // any CSS you import will output into a single css file (app.css in this case)
import './styles/app.scss'; import './styles/app.scss';
const $ = require('jquery');
// start the Stimulus application // start the Stimulus application
require('bootstrap'); require('bootstrap');
@ -18,27 +17,32 @@ import '@fortawesome/fontawesome-free/js/solid'
import '@fortawesome/fontawesome-free/js/regular' import '@fortawesome/fontawesome-free/js/regular'
import '@fortawesome/fontawesome-free/js/brands' import '@fortawesome/fontawesome-free/js/brands'
// Friendly captcha
import { WidgetInstance } from 'friendly-challenge';
function doneCallback(solution) {
$('#registration_form_captcha_solution').val(solution);
}
const element = document.querySelector('#captcha');
const options = {
doneCallback: doneCallback,
sitekey: 'FCMVL79DP1G5K1K0',
}
const widget = new WidgetInstance(element, options);
widget.start()
// Dsiplay Filename when uploading
document.querySelector('.custom-file-input').addEventListener('change', function (e) {
var fileName = document.getElementById('offer_form_photo').files[0].name;
var nextSibling = e.target.nextElementSibling
nextSibling.innerText = fileName
})
// Cookie-consent // Cookie-consent
import 'cookie-notice/dist/cookie.notice.min.js' import 'cookie-notice/dist/cookie.notice.min';
new cookieNoticeJS({
// Position for the cookie-notifier (default=bottom)
'cookieNoticePosition': 'bottom',
// The message will be shown again in X days
'expiresIn': 365,
// Specify a custom font family and size in pixels
'fontFamily': 'inherit',
'fontSize': '.9rem',
// Dismiss button background color
'buttonBgColor': '#343a40',
// Dismiss button text color
'buttonTextColor': '#fff',
// Notice background color
'noticeBgColor': '#000',
// Notice text color
'noticeTextColor': '#fff',
// Print debug output to the console (default=false)
'debug': false
});

15
assets/captcha.js Normal file
View file

@ -0,0 +1,15 @@
// Friendly captcha
import { WidgetInstance } from 'friendly-challenge';
const $ = require('jquery');
function doneCallback(solution) {
$('#registration_form_captcha_solution').val(solution);
}
const element = document.querySelector('#captcha');
const options = {
doneCallback: doneCallback,
sitekey: 'FCMVL79DP1G5K1K0',
}
const widget = new WidgetInstance(element, options);
widget.start()

5
assets/fileUpload.js Normal file
View file

@ -0,0 +1,5 @@
const $ = require('jquery');
$( ".custom-file-input" ).change(function() {
$(".custom-file-label").html(($(".custom-file-input").prop("files")[0]["name"]));
});

View file

@ -8,6 +8,7 @@
"ext-ctype": "*", "ext-ctype": "*",
"ext-iconv": "*", "ext-iconv": "*",
"composer/package-versions-deprecated": "1.11.99.1", "composer/package-versions-deprecated": "1.11.99.1",
"deployer/deployer": "^7.0",
"doctrine/doctrine-bundle": "^2.3", "doctrine/doctrine-bundle": "^2.3",
"doctrine/doctrine-migrations-bundle": "^3.1", "doctrine/doctrine-migrations-bundle": "^3.1",
"doctrine/orm": "^2.8", "doctrine/orm": "^2.8",
@ -17,26 +18,26 @@
"presta/sitemap-bundle": "^3.2", "presta/sitemap-bundle": "^3.2",
"samayo/bulletproof": "4.0.1", "samayo/bulletproof": "4.0.1",
"sensio/framework-extra-bundle": "^6.1", "sensio/framework-extra-bundle": "^6.1",
"symfony/asset": "5.4.*", "symfony/asset": "^5.4.20",
"symfony/console": "5.4.*", "symfony/console": "^5.4.20",
"symfony/dotenv": "5.4.*", "symfony/dotenv": "^5.4.20",
"symfony/filesystem": "5.4.*", "symfony/filesystem": "^5.4.20",
"symfony/flex": "^1.3.1", "symfony/flex": "^1.3.1",
"symfony/form": "5.4.*", "symfony/form": "^5.4.20",
"symfony/framework-bundle": "5.4.*", "symfony/framework-bundle": "^5.4.20",
"symfony/mailer": "5.4.*", "symfony/mailer": "^5.4.20",
"symfony/monolog-bundle": "^3.7", "symfony/monolog-bundle": "^3.7",
"symfony/proxy-manager-bridge": "5.4.*", "symfony/proxy-manager-bridge": "^5.4.20",
"symfony/security-bundle": "5.4.*", "symfony/security-bundle": "^5.4.20",
"symfony/twig-bundle": "5.4.*", "symfony/twig-bundle": "^5.4.20",
"symfony/validator": "5.4.*", "symfony/validator": "^5.4.20",
"symfony/webpack-encore-bundle": "^1.11", "symfony/webpack-encore-bundle": "^1.11",
"symfony/yaml": "5.4.*", "symfony/yaml": "^5.4.20",
"symfonycasts/reset-password-bundle": "^1.7", "symfonycasts/reset-password-bundle": "^1.7",
"symfonycasts/verify-email-bundle": "^1.4", "symfonycasts/verify-email-bundle": "^1.4",
"twig/extra-bundle": "^2.12|^3.0", "twig/extra-bundle": "^2.12|^3.0",
"twig/intl-extra": "^3.3", "twig/intl-extra": "^3.3",
"twig/twig": "^2.12|^3.0" "twig/twig": "^3.4.3"
}, },
"config": { "config": {
"optimize-autoloader": true, "optimize-autoloader": true,
@ -82,11 +83,10 @@
"extra": { "extra": {
"symfony": { "symfony": {
"allow-contrib": false, "allow-contrib": false,
"require": "5.4.*" "require": "^5.4.20"
} }
}, },
"require-dev": { "require-dev": {
"deployer/dist": "^6.8",
"symfony/browser-kit": "^5.4", "symfony/browser-kit": "^5.4",
"symfony/css-selector": "^5.4", "symfony/css-selector": "^5.4",
"symfony/debug-bundle": "^5.4", "symfony/debug-bundle": "^5.4",

1894
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,10 @@
<?php <?php
namespace Deployer; namespace Deployer;
require 'recipe/symfony4.php'; require 'recipe/symfony.php';
// Project name // Project name
set('application', 'beta.pflaenz.li'); set('application', 'pflaenz.li');
// Project repository // Project repository
set('repository', 'ssh://git@git.thisfro.ch:222/thisfro/pflaenz.li.git'); set('repository', 'ssh://git@git.thisfro.ch:222/thisfro/pflaenz.li.git');
@ -13,11 +13,11 @@ set('repository', 'ssh://git@git.thisfro.ch:222/thisfro/pflaenz.li.git');
set('git_tty', true); set('git_tty', true);
set('bin/php', function() { set('bin/php', function() {
return '/opt/php8.0/bin/php'; return '/opt/php8.2/bin/php';
}); });
set('bin/composer', function() { set('bin/composer', function() {
return '/opt/php8.0/bin/composer2'; return '/opt/php8.2/bin/composer2';
}); });
// Shared files/dirs between deploys // Shared files/dirs between deploys
@ -28,14 +28,14 @@ add('shared_dirs', ['public/uploads']);
add('writable_dirs', []); add('writable_dirs', []);
// Set composer options // Set composer options
set('composer_options', '{{composer_action}} --verbose --prefer-dist --no-progress --no-interaction --optimize-autoloader --no-scripts --no-dev'); set('composer_options', '--verbose --prefer-dist --no-progress --no-interaction --optimize-autoloader --no-scripts');
// Hosts // Hosts
host('beta.xn--pflnz-ira.li') host('lq5xi.ftp.infomaniak.com')
->user('lq5xi_thisfro') ->set('remote_user', 'lq5xi_thisfro')
->set('deploy_path', '~/sites/{{application}}') ->set('deploy_path', '~/sites/{{stage}}.{{application}}')
->set('http_user', 'uid153060') ->set('http_user', 'uid153060')
->stage('beta'); ->set('stage', 'beta');
// Tasks // Tasks
@ -45,9 +45,9 @@ task('upload:build', function() {
// Build yarn locally // Build yarn locally
task('deploy:build:assets', function (): void { task('deploy:build:assets', function (): void {
run('yarn install'); runLocally('yarn install');
run('yarn encore production'); runLocally('yarn encore production');
})->local()->desc('Install front-end assets'); })->desc('Install front-end assets');
before('deploy:symlink', 'deploy:build:assets'); before('deploy:symlink', 'deploy:build:assets');

6689
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -19,11 +19,12 @@
"dev-server": "encore dev-server", "dev-server": "encore dev-server",
"dev": "encore dev", "dev": "encore dev",
"watch": "encore dev --watch", "watch": "encore dev --watch",
"build": "encore production --progress" "build": "encore production --progress",
"test": "snyk test"
}, },
"dependencies": { "dependencies": {
"@snyk/protect": "^1.834.0",
"cookie-notice": "^1.3.6", "cookie-notice": "^1.3.6",
"friendly-challenge": "^0.8.5", "friendly-challenge": "^0.8.5"
"snyk": "^1.806.0"
} }
} }

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

View file

@ -18,5 +18,7 @@ if ($_SERVER['APP_DEBUG']) {
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
$request = Request::createFromGlobals(); $request = Request::createFromGlobals();
$response = $kernel->handle($request); $response = $kernel->handle($request);
$response->headers->set('X-Frame-Options', 'DENY');
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->send(); $response->send();
$kernel->terminate($request, $response); $kernel->terminate($request, $response);

View file

@ -33,15 +33,21 @@ class OfferController extends AbstractController
} }
#[Route('/offers', name: 'offers', options: ["sitemap" => true])] #[Route('/offers', name: 'offers', options: ["sitemap" => true])]
public function showAll(Environment $twig, Request $request, OfferRepository $offerRepository, PlzToCoordinate $plzconverter, DistanceCalculator $distanceCalculator): Response public function showAll(Request $request, OfferRepository $offerRepository, PlzToCoordinate $plzconverter, DistanceCalculator $distanceCalculator): Response
{ {
$form = $this->createForm(OfferFilterFormType::class); $form = $this->createForm(OfferFilterFormType::class);
$form->handleRequest($request); $form->handleRequest($request);
$allOffers = $offerRepository->findAll();
$filteredOffers = []; $filteredOffers = [];
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid() && $form->get('search')->getData() != null) {
$allOffers = $offerRepository->findBySearchLiteral($form->get('search')->getData());
}
else {
$allOffers = $offerRepository->findAll();
}
if ($form->isSubmitted() && $form->isValid() && $form->get('distance')->getData() != null && $form->get('zipCode')->getData() != null) {
$filterDistance = $form->get('distance')->getData(); $filterDistance = $form->get('distance')->getData();
$filterPlz = $form->get('zipCode')->getData(); $filterPlz = $form->get('zipCode')->getData();
$filterCoordinate = $plzconverter->convertPlzToCoordinate($filterPlz); $filterCoordinate = $plzconverter->convertPlzToCoordinate($filterPlz);
@ -144,7 +150,7 @@ class OfferController extends AbstractController
} }
#[Route('/offer/edit/{urlId}', name: 'edit_offer')] #[Route('/offer/edit/{urlId}', name: 'edit_offer')]
public function editOffer(Offer $offer, OfferRepository $offerRepository, Request $request, string $photoDir, OfferPhotoHelper $offerPhotoHelper): Response public function editOffer(Offer $offer, Request $request, string $photoDir, OfferPhotoHelper $offerPhotoHelper): Response
{ {
$form = $this->createForm(OfferFormType::class, $offer); $form = $this->createForm(OfferFormType::class, $offer);
$user = $this->getUser(); $user = $this->getUser();
@ -164,6 +170,10 @@ class OfferController extends AbstractController
$this->entityManager->persist($offer); $this->entityManager->persist($offer);
$this->entityManager->flush(); $this->entityManager->flush();
$this->addFlash("success", "Successfully updated the offer!");
return $this->redirectToRoute('show_offer', ['urlId' => $offer->getUrlId()]);
} }
return $this->render('offer/edit.html.twig', [ return $this->render('offer/edit.html.twig', [
@ -209,4 +219,14 @@ class OfferController extends AbstractController
'offers' => $offerRepository->findByUser($user), 'offers' => $offerRepository->findByUser($user),
]); ]);
} }
#[Route('/offers/search', name: 'search', options: ["sitemap" => false])]
public function search(OfferRepository $offerRepository): Response
{
$offers = $offerRepository->findBySearchLiteral('');
return $this->render('offer/search.html.twig', [
'offers' => $offers,
]);
}
} }

View file

@ -71,7 +71,7 @@ class ResetPasswordController extends AbstractController
* Validates and process the reset URL that the user clicked in their email. * Validates and process the reset URL that the user clicked in their email.
*/ */
#[Route('/reset/{token}', name: 'app_reset_password')] #[Route('/reset/{token}', name: 'app_reset_password')]
public function reset(Request $request, UserPasswordHasherInterface $passwordHasher, string $token = null): Response public function reset(Request $request, UserPasswordHasherInterface $passwordEncoder, string $token = null): Response
{ {
if ($token) { if ($token) {
// We store the token in session and remove it from the URL, to avoid the URL being // We store the token in session and remove it from the URL, to avoid the URL being
@ -106,12 +106,13 @@ class ResetPasswordController extends AbstractController
$this->resetPasswordHelper->removeResetRequest($token); $this->resetPasswordHelper->removeResetRequest($token);
// Encode the plain password, and set it. // Encode the plain password, and set it.
$encodedPassword = $passwordHasher->encodePassword( $user->setPassword(
$passwordEncoder->hashPassword(
$user, $user,
$form->get('plainPassword')->getData() $form->get('plainPassword')->getData()
)
); );
$user->setPassword($encodedPassword);
$this->getDoctrine()->getManager()->flush(); $this->getDoctrine()->getManager()->flush();
// The session is cleaned up after the password has been changed. // The session is cleaned up after the password has been changed.

View file

@ -14,11 +14,17 @@ class OfferFilterFormType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options): void public function buildForm(FormBuilderInterface $builder, array $options): void
{ {
$builder $builder
->add('zipCode', TextType::class, [ ->add('search', TextType::class, [
'label' => 'ZIP', 'label' => '<i class="fas fa-search mr-1"></i>Search',
'label_html' => true,
])
->add('zipCode', NumberType::class, [
'label' => '<i class="fas fa-map-marker-alt mr-2"></i>ZIP',
'label_html' => true,
]) ])
->add('distance', NumberType::class, [ ->add('distance', NumberType::class, [
'label' => 'Distance', 'label' => '<i class="fas fa-map-signs mr-1"></i>Distance',
'label_html' => true,
]) ])
->add('Apply', SubmitType::class) ->add('Apply', SubmitType::class)
; ;

View file

@ -46,6 +46,8 @@ class RegistrationFormType extends AbstractType
]) ])
->add('agreeTerms', CheckboxType::class, [ ->add('agreeTerms', CheckboxType::class, [
'mapped' => false, 'mapped' => false,
'label' => 'Agree to <a href="/imprint" target="_blank">Terms</a>',
'label_html' => true,
'constraints' => [ 'constraints' => [
new IsTrue([ new IsTrue([
'message' => 'You need to agree to our terms.', 'message' => 'You need to agree to our terms.',

View file

@ -44,6 +44,19 @@ class OfferRepository extends ServiceEntityRepository
; ;
} }
public function findBySearchLiteral(string $literal)
{
$qb = $this->createQueryBuilder('o');
$qb->andWhere($qb->expr()->like('o.title', ':lit'))
->setParameter('lit', '%' . $literal . '%')
->orderBy('o.id', 'ASC')
;
$qb = $qb->getQuery()->getResult();
return $qb;
}
/* /*
public function findOneBySomeField($value): ?Offer public function findOneBySomeField($value): ?Offer
{ {

View file

@ -348,15 +348,9 @@
"symfony/polyfill-mbstring": { "symfony/polyfill-mbstring": {
"version": "v1.22.1" "version": "v1.22.1"
}, },
"symfony/polyfill-php73": {
"version": "v1.22.1"
},
"symfony/polyfill-php80": { "symfony/polyfill-php80": {
"version": "v1.22.1" "version": "v1.22.1"
}, },
"symfony/polyfill-php81": {
"version": "v1.23.0"
},
"symfony/polyfill-uuid": { "symfony/polyfill-uuid": {
"version": "v1.22.1" "version": "v1.22.1"
}, },
@ -404,9 +398,6 @@
"symfony/security-csrf": { "symfony/security-csrf": {
"version": "v5.2.4" "version": "v5.2.4"
}, },
"symfony/security-guard": {
"version": "v5.2.4"
},
"symfony/security-http": { "symfony/security-http": {
"version": "v5.2.6" "version": "v5.2.6"
}, },

View file

@ -2,6 +2,10 @@
{% block title %}New Offer{% endblock %} {% block title %}New Offer{% endblock %}
{% block javascripts %}
{{ encore_entry_script_tags('fileUpload') }}
{% endblock %}
{% block body %} {% block body %}
{% for message in app.flashes('error') %} {% for message in app.flashes('error') %}
<div class="alert alert-error" role="alert"> <div class="alert alert-error" role="alert">

View file

@ -41,10 +41,10 @@
</p> </p>
<p class="pr-3"> <p class="pr-3">
<i class="fas fa-map-marker-alt"></i> {{ offer.zipCode }} <i class="fas fa-map-marker-alt"></i> {{ offer.zipCode }}
{% if distance > 0 %}
(ca. {{ distance }} km)
{% endif %}
</p> </p>
{% if distance > 0 %}
<p class="pr-3"><i class="fas fa-map-signs mr-1"></i>ca. {{ distance }} km</p>
{% endif %}
</div> </div>
<h3>Description</h3> <h3>Description</h3>
<p>{{ offer.description }}</p> <p>{{ offer.description }}</p>

View file

@ -10,13 +10,14 @@
<meta name="robots" content="index,follow" /> <meta name="robots" content="index,follow" />
{% endif %} {% endif %}
<meta name="publisher" content="pflänz.li" /> <meta name="publisher" content="pflänz.li" />
<meta name="keywords" content="trade, share, plants", "sustainability" /> <meta name="keywords" content="trade, share, plants, sustainability, pflanzentausch, pflanzen" />
{% block stylesheets %} {% block stylesheets %}
{{ encore_entry_link_tags('app') }} {{ encore_entry_link_tags('app') }}
{% endblock %} {% endblock %}
{% block javascripts %} {% block javascripts %}{% endblock %}
{{ encore_entry_script_tags('app') }} {{ encore_entry_script_tags('app') }}
<!-- Matomo --> <!-- Matomo -->
@ -26,7 +27,7 @@
_paq.push(['trackPageView']); _paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']); _paq.push(['enableLinkTracking']);
(function () { (function () {
var u = "//analytics.thisfro.ch/"; var u = "https://analytics.thisfro.ch/";
_paq.push([ _paq.push([
'setTrackerUrl', 'setTrackerUrl',
u + 'matomo.php' u + 'matomo.php'
@ -41,7 +42,6 @@ s.parentNode.insertBefore(g, s);
})(); })();
</script> </script>
<!-- End Matomo Code --> <!-- End Matomo Code -->
{% endblock %}
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
</head> </head>
@ -110,11 +110,11 @@ s.parentNode.insertBefore(g, s);
</a> </a>
</section> </section>
</div> </div>
<div class="col-lg pt-4"> <div class="col-lg pt-4 pl-5">
<section> <section>
<h2 class="h5">Links</h2> <h2 class="h5">Links</h2>
<ul class="link-list"> <ul class="link-list">
<li><a href="https://blog.pflänz.li">Blog</a></li> <li><a href="https://blog.pflaenz.li">Blog</a></li>
<li><a href="{{ path('imprint') }}">Imprint</a></li> <li><a href="{{ path('imprint') }}">Imprint</a></li>
<li><a href="{{ path('faq') }}">FAQ</a></li> <li><a href="{{ path('faq') }}">FAQ</a></li>
</ul> </ul>

View file

@ -20,7 +20,7 @@
<div class="btn btn-primary"><i class="fas fa-filter mr-3"></i>Filter<i class="fas fa-chevron-down ml-3 dropdown-collapse"></i></div> <div class="btn btn-primary"><i class="fas fa-filter mr-3"></i>Filter<i class="fas fa-chevron-down ml-3 dropdown-collapse"></i></div>
</a> </a>
<div class="collapse" id="collapseExample"> <div class="collapse" id="collapseExample">
{{ form(filter_form) }} {{ form(filter_form, {attr: {novalidate: 'novalidate'}}) }}
</div> </div>
</div> </div>

View file

@ -4,7 +4,11 @@
{% endblock %} {% endblock %}
{% block meta %} {% block meta %}
<meta name="description" content="Register for pflänz.li" <meta name="description" content="Register for pflänz.li" />
{% endblock %}
{% block javascripts %}
{{ encore_entry_script_tags('captcha') }}
{% endblock %} {% endblock %}
{% block body %} {% block body %}

View file

@ -6,7 +6,6 @@
<h1>Reset your password</h1> <h1>Reset your password</h1>
{{ form_start(resetForm) }} {{ form_start(resetForm) }}
{{ form_row(resetForm.plainPassword) }} {{form_widget(resetForm)}}
<button class="btn btn-primary">Reset password</button>
{{ form_end(resetForm) }} {{ form_end(resetForm) }}
{% endblock %} {% endblock %}

View file

@ -21,6 +21,8 @@ Encore
* and one CSS file (e.g. app.css) if your JavaScript imports CSS. * and one CSS file (e.g. app.css) if your JavaScript imports CSS.
*/ */
.addEntry('app', './assets/app.js') .addEntry('app', './assets/app.js')
.addEntry('captcha', './assets/captcha.js')
.addEntry('fileUpload', './assets/fileUpload.js')
// enables the Symfony UX Stimulus bridge (used in assets/bootstrap.js) // enables the Symfony UX Stimulus bridge (used in assets/bootstrap.js)
.enableStimulusBridge('./assets/controllers.json') .enableStimulusBridge('./assets/controllers.json')

View file

@ -936,6 +936,11 @@
error-stack-parser "^2.0.0" error-stack-parser "^2.0.0"
string-width "^4.2.3" string-width "^4.2.3"
"@snyk/protect@^1.834.0":
version "1.834.0"
resolved "https://registry.npmjs.org/@snyk/protect/-/protect-1.834.0.tgz"
integrity sha512-I/zzykVqRI4ZeIGwhwnQ/li01W0fJC6uMGdM6oGWOIOex3L6BBz2LTZeHr4PMoZDNha2TM10hgcYf9JvGvjNKQ==
"@stimulus/core@^2.0.0": "@stimulus/core@^2.0.0":
version "2.0.0" version "2.0.0"
resolved "https://registry.npmjs.org/@stimulus/core/-/core-2.0.0.tgz" resolved "https://registry.npmjs.org/@stimulus/core/-/core-2.0.0.tgz"
@ -1593,9 +1598,9 @@ caniuse-api@^3.0.0:
lodash.uniq "^4.5.0" lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001286: caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001286:
version "1.0.30001287" version "1.0.30001388"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001287.tgz" resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001388.tgz"
integrity sha512-4udbs9bc0hfNrcje++AxBuc6PfLNHwh3PO9kbwnfCQWyqtlzg3py0YgFu8jyRTTo85VAz4U+VLxSlID09vNtWA== integrity sha512-znVbq4OUjqgLxMxoNX2ZeeLR0d7lcDiE5uJ4eUiWdml1J1EkxbnQq6opT9jb9SMfJxB0XA16/ziHwni4u1I3GQ==
chalk@^2.0.0, chalk@^2.3.2, chalk@^2.4.2: chalk@^2.0.0, chalk@^2.3.2, chalk@^2.4.2:
version "2.4.2" version "2.4.2"
@ -4197,11 +4202,6 @@ slash@^3.0.0:
resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
snyk@^1.806.0:
version "1.806.0"
resolved "https://registry.npmjs.org/snyk/-/snyk-1.806.0.tgz"
integrity sha512-X0Aso0+zA9YXrrIgW1G3GXRqsvW4j7gXS9QyxFUwPp9qp5dAX1sjNUuLrK/z0CpuIpT7MVOBrYHF/RkMJ2C+FA==
sockjs@^0.3.21: sockjs@^0.3.21:
version "0.3.24" version "0.3.24"
resolved "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz" resolved "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz"