<?php
namespace App\Controller;
use App\Blog;
use App\Content;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
class MarketingController extends AbstractController
{
/**
* List of the available customer logos for the landing page.
*/
public static array $customerLogos = [
[
'img' => 'massmutual.png',
'name' => 'Mass Mutual',
],
[
'img' => 'tmobile.png',
'name' => 'T-Mobile',
],
[
'img' => 'boysgirlsclub.png',
'name' => 'Boys & Girls Club',
],
[
'img' => 'convene.png',
'name' => 'Convene',
],
[
'img' => 'reputation-nw.jpg',
'name' => 'Reputation.com',
],
[
'img' => 'berkshirehathaway.png',
'name' => 'Berkshire Hathaway',
],
[
'img' => 'gvr.png',
'name' => 'Gilbarco Veeder-Root',
],
[
'img' => 'solutionreach.png',
'name' => 'SolutionReach',
],
[
'img' => 'seniorly.png',
'name' => 'Seniorly',
],
[
'img' => 'covetrus.png',
'name' => 'Covetrus',
],
[
'img' => 'mutesix.png',
'name' => 'MuteSix',
],
[
'img' => 'crispvideo.png',
'name' => 'Crisp Video',
],
];
/**
* List of the available customer logos for the healthcare page.
*/
public static array $customerLogosHealthcare = [
[
'img' => 'axiom-lg.png',
'name' => 'Axiom Medical Consulting',
],
[
'img' => 'SolutionReach logo.png',
'name' => 'SolutionReach',
],
[
'img' => 'Metagenics logo.png',
'name' => 'Metagenics',
],
[
'img' => 'Medicopy Logo.png',
'name' => 'Medicopy',
],
[
'img' => 'Ambra Health Logo.png',
'name' => 'Ambra Health',
],
[
'img' => 'Thyroid Virtual Clinic Logo.png',
'name' => 'Thyroid Virtual Clinic',
],
[
'img' => 'Lifematters Logo.png',
'name' => 'Lifematters',
],
];
/**
* List of the available customer logos for the healthcare page.
*/
public static array $customerLogosInsurance = [
[
'img' => 'ITM TwentyFirst Logo.png',
'name' => 'ITM TwentyFirst',
],
[
'img' => 'Inshur Logo.png',
'name' => 'Inshur',
],
[
'img' => 'Cypress Insurance Logo.png',
'name' => 'Cypress Insurance',
],
[
'img' => 'ARGI Logo.png',
'name' => 'ARGI',
],
];
/**
* List of the available customer logos for the healthcare page.
*/
public static array $customerLogosTech = [
[
'img' => 'uber.png',
'name' => 'Uber',
],
[
'img' => 'Vonigo Logo.png',
'name' => 'Vonigo',
],
[
'img' => 'Trimble Logo.png',
'name' => 'Trimble',
],
[
'img' => 'Wisely Logo.png',
'name' => 'Wisely',
],
[
'img' => 'chowly.png',
'name' => 'Chowly',
],
[
'img' => 'e2open Logo.png',
'name' => 'e2open',
],
[
'img' => 'Starmind Logo.png',
'name' => 'Starmind',
],
[
'img' => 'Evercast Logo.png',
'name' => 'Evercast',
],
[
'img' => 'Tempo Automation Logo.png',
'name' => 'Tempo Automation',
],
[
'img' => 'Restream Logo.png',
'name' => 'Restream',
],
[
'img' => 'Vidyard Logo.png',
'name' => 'Vidyard',
],
];
/**
* List of the available awards badges.
*/
public static array $awardsBadges = [
[
'img' => 'capterra21.png',
'name' => 'Capterra',
],
[
'img' => 'cloud-awards21.png',
'name' => 'Cloud Awards',
],
[
'img' => 'cpa-choice21.png',
'name' => 'CPA Readers Choice',
],
[
'img' => 'getapp21.png',
'name' => 'GetApp Category Leaders',
],
[
'img' => 'leader-summer21.png',
'name' => 'Leader Summer 2021',
],
[
'img' => 'sa-fr21.png',
'name' => 'Software Advice Front Runners',
],
[
'img' => 'saasworthy21.png',
'name' => 'SaasWorty Top 20',
],
[
'img' => 'corpvis21.jpg',
'name' => 'Corporate Vision Excellence Award',
],
[
'img' => 'i5k.png',
'name' => 'Inc. 5000',
],
[
'img' => 'forbes21.png',
'name' => 'Forbes Official Member',
],
];
/**
* Mapping of the feature page redirects.
* Old -> New.
*/
private const FEATURE_REDIRECTS = [
'invoicing' => 'invoice-to-cash',
'subscription-billing' => 'subscription-billing',
'payments' => 'payment-plans',
'billing-portal' => 'customer-portal',
];
#[Route(path: '/login', name: 'login_redirect', methods: ['GET'])]
public function loginRedirect(): Response
{
return new RedirectResponse($this->getParameter('app.dashboard_url'));
}
#[Route(path: '/signup', name: 'signup_redirect', methods: ['GET'])]
public function signupRedirect(Request $request): Response
{
$url = 'https://invoiced.com/signup';
if ($query = $request->query->all()) {
$url .= '?'.http_build_query($query);
}
return new RedirectResponse($url);
}
#[Route(path: '/', name: 'landing', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => 1, 'sitemap_changefreq' => 'daily'])]
public function index(Blog $blog): Response
{
return $this->render('landing.twig', [
'logos' => $this->getCustomerLogos(),
'latestBlogPosts' => $blog->getPosts(2),
'badges' => $this->getAwardsBadges(),
]);
}
private function getCustomerLogos(): array
{
$logos = self::$customerLogos;
$logos = array_slice($logos, 0, 12);
foreach ($logos as &$logo) {
$logo['img'] = '/img/landing2/customer-logos/'.$logo['img'];
}
return $logos;
}
private function getAwardsBadges(): array
{
$badges = self::$awardsBadges;
$badges = array_slice($badges, 0, 10);
foreach ($badges as &$badge) {
$badge['img'] = '/img/badges/'.$badge['img'];
}
return $badges;
}
#[Route(path: '/features', name: 'shortcut_features', methods: ['GET'])]
public function shortcutFeatures(): Response
{
return new RedirectResponse('/product', 301);
}
#[Route(path: '/features/{section}', name: 'shortcut_features_section', methods: ['GET'])]
public function shortcutFeaturesSection($section): Response
{
if (isset(self::FEATURE_REDIRECTS[$section])) {
return new RedirectResponse('/product/'.self::FEATURE_REDIRECTS[$section], 301);
}
return new RedirectResponse('/product', 301);
}
#[Route(path: '/product', name: 'product', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => 1, 'sitemap_changefreq' => 'weekly'])]
public function product(): Response
{
return $this->render('product/product.twig', [
'logos' => $this->getCustomerLogos(),
'badges' => $this->getAwardsBadges(),
]);
}
#[Route(path: '/product/cash-application', name: 'product_cash_application', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'weekly'])]
public function productCashApplication(): Response
{
return $this->render('features/cash-application.twig', [
'logos' => $this->getCustomerLogos(),
'badges' => $this->getAwardsBadges(),
]);
}
#[Route(path: '/product/customer-portal', name: 'product_customer_portal', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'weekly'])]
public function productCustomerPortal(): Response
{
return $this->render('features/customer-portal.twig', [
'logos' => $this->getCustomerLogos(),
'badges' => $this->getAwardsBadges(),
]);
}
#[Route(path: '/product/erp-connect', name: 'product_erp_connect', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'weekly'])]
public function productErpConnect(): Response
{
return $this->render('features/erp-connect.twig');
}
#[Route(path: '/product/estimates', name: 'product_estimates', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'weekly'])]
public function productEstimates(): Response
{
return $this->render('features/estimates.twig', [
'logos' => $this->getCustomerLogos(),
'badges' => $this->getAwardsBadges(),
]);
}
#[Route(path: '/gateways', name: 'shortcut_payment_gateways', methods: ['GET'])]
public function shortcutPaymentGateways(): Response
{
return new RedirectResponse('/product/integrations#payment-gateways', 301);
}
#[Route(path: '/integrations', name: 'shortcut_integrations', methods: ['GET'])]
public function shortcutIntegrations(): Response
{
return new RedirectResponse('/product/integrations', 301);
}
#[Route(path: '/product/integrations', name: 'product_integrations', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'weekly'])]
public function integrations(): Response
{
return $this->render('features/integrations.twig');
}
#[Route(path: '/product/invoice-to-cash', name: 'product_invoice_to_cash', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'weekly'])]
public function productInvoiceToCash(): Response
{
return $this->render('features/invoice-to-cash.twig', [
'logos' => $this->getCustomerLogos(),
'badges' => $this->getAwardsBadges(),
]);
}
#[Route(path: '/product/payment-plans', name: 'product_payment_plans', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'weekly'])]
public function productPaymentPlans(): Response
{
return $this->render('features/payment-plans.twig', [
'logos' => $this->getCustomerLogos(),
'badges' => $this->getAwardsBadges(),
]);
}
#[Route(path: '/security', name: 'shortcut_security', methods: ['GET'])]
public function shortcutSecurity(): Response
{
return new RedirectResponse('/product/security', 301);
}
#[Route(path: '/product/security', name: 'security', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.5', 'sitemap_changefreq' => 'monthly'])]
public function security(): Response
{
return $this->render('security.twig');
}
#[Route(path: '/product/subscription-billing', name: 'product_subscription_billing', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'weekly'])]
public function productSubscriptionBilling(): Response
{
return $this->render('features/subscription-billing.twig', [
'logos' => $this->getCustomerLogos(),
'badges' => $this->getAwardsBadges(),
]);
}
#[Route(path: '/product/recurring-billing', name: 'shortcut_recurring_billing', methods: ['GET'])]
public function shortcutRecurringBilling(): Response
{
return new RedirectResponse('/product/subscription-billing', 301);
}
#[Route(path: '/payments', name: 'payments', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => 1, 'sitemap_changefreq' => 'weekly'])]
public function payments(): Response
{
return $this->render('payments.twig', [
'logos' => $this->getCustomerLogos(),
'badges' => $this->getAwardsBadges(),
]);
}
#[Route(path: '/pricing', name: 'pricing', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.25', 'sitemap_changefreq' => 'weekly'])]
public function pricing(): Response
{
return $this->render('pricing.twig');
}
#[Route(path: '/partner', name: 'shortcut_partner', methods: ['GET'])]
public function shortcutPartner(): Response
{
return new RedirectResponse('/about/partners', 301);
}
#[Route(path: '/about/partners', name: 'partner', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.5', 'sitemap_changefreq' => 'monthly'])]
public function partner(): Response
{
return $this->render('partner.twig');
}
#[Route(path: '/about', name: 'about', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.5', 'sitemap_changefreq' => 'monthly'])]
public function about(): Response
{
return $this->render('about.twig', [
'logos' => $this->getCustomerLogos(),
'badges' => $this->getAwardsBadges(),
]);
}
#[Route(path: '/resources', name: 'resources', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.5', 'sitemap_changefreq' => 'monthly'])]
public function resources(): Response
{
return $this->render('resources/resources.twig');
}
#[Route(path: '/customers', name: 'shortcut_customers', methods: ['GET'])]
public function shortcutCustomers(): Response
{
return new RedirectResponse('/resources/customers', 301);
}
#[Route(path: '/resources/customers', name: 'customers', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'weekly'])]
public function customers(Content $content): Response
{
return $this->render('resources/customers.twig', [
'caseStudies' => $content->getCaseStudies(),
'logos' => $this->getCustomerLogos(),
]);
}
#[Route(path: '/customers/{id}', name: 'shortcut_case_study', methods: ['GET'])]
public function shortcutCaseStudy(string $id): Response
{
return new RedirectResponse('/resources/customers/'.$id, 301);
}
#[Route(path: '/resources/customers/{id}', name: 'case_study', methods: ['GET'])]
public function caseStudy(string $id, Content $content): Response
{
// look up
$caseStudy = $content->getCaseStudy($id);
if (!$caseStudy) {
throw new NotFoundHttpException();
}
return $this->render('case-studies/'.$id.'.twig', [
'caseStudy' => $caseStudy,
'caseStudyId' => $id,
]);
}
#[Route(path: '/resources/data-sheets', name: 'data_sheets', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'monthly'])]
public function dataSheets(): Response
{
return $this->render('resources/data-sheets.twig');
}
#[Route(path: '/resources/tools', name: 'tools', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'monthly'])]
public function tools(): Response
{
return $this->render('resources/tools.twig');
}
#[Route(path: '/resources/webinars', name: 'webinars', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'weekly'])]
public function webinars(Content $content): Response
{
return $this->render('resources/webinars.twig', [
'webinars' => $content->getWebinars(),
]);
}
#[Route(path: '/resources/videos/{id}', name: 'video', methods: ['GET'])]
public function video(string $id, Content $content): Response
{
$video = $content->getVideo($id);
if (!$video) {
throw new NotFoundHttpException();
}
if (isset($video['link'])) {
return new RedirectResponse($video['link']);
}
return $this->render('resources/video.twig', [
'video' => $video,
]);
}
#[Route(path: '/resources/videos', name: 'videos', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'weekly'])]
public function videos(Content $content): Response
{
return $this->render('resources/videos.twig', [
'videos' => $content->getVideos(),
]);
}
#[Route(path: '/resources/webinars/{id}', name: 'webinar', methods: ['GET'])]
public function webinar(string $id, Content $content): Response
{
$webinar = $content->getWebinar($id);
if (!$webinar) {
throw new NotFoundHttpException();
}
return $this->render('resources/webinar.twig', [
'webinar' => $webinar,
]);
}
#[Route(path: '/resources/white-papers', name: 'whitepapers', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'weekly'])]
public function whitepapers(Content $content): Response
{
return $this->render('resources/white-papers.twig', [
'whitepapers' => $content->getWhitePapers(),
]);
}
#[Route(path: '/resources/white-papers/thanks', name: 'white_paper_thanks', methods: ['GET'])]
public function whitePaperThanks(): Response
{
return $this->render('resources/white-paper-thanks.twig');
}
#[Route(path: '/resources/white-papers/{id}', name: 'white_paper', methods: ['GET'])]
public function whitePaper(string $id, Content $content): Response
{
$whitePaper = $content->getWhitePaper($id);
if (!$whitePaper) {
throw new NotFoundHttpException();
}
if (isset($whitePaper['link'])) {
return new RedirectResponse($whitePaper['link']);
}
return $this->render('resources/white-paper.twig', [
'whitePaper' => $whitePaper,
]);
}
#[Route(path: '/careers', name: 'shortcut_careers', methods: ['GET'])]
public function shortcutCareers(): Response
{
return new RedirectResponse('/about/careers', 301);
}
#[Route(path: '/about/careers', name: 'careers', methods: ['GET'])]
public function careers(): Response
{
return new RedirectResponse('https://apply.workable.com/invoiced-1');
}
#[Route(path: '/about/press', name: 'press', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'monthly'])]
public function press(): Response
{
return $this->render('press.twig');
}
#[Route(path: '/solutions', name: 'solutions', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'monthly'])]
public function solutions(): Response
{
return $this->render('solutions.twig');
}
#[Route(path: '/solutions/agencies', name: 'solution_agencies', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'monthly'])]
public function solutionAgencies(): Response
{
return $this->render('solutions/agencies.twig', [
'logos' => $this->getCustomerLogosTech(),
'badges' => $this->getAwardsBadges(),
]);
}
private function getCustomerLogosTech(): array
{
$logos = self::$customerLogosTech;
$logos = array_slice($logos, 0, 11);
foreach ($logos as &$logo) {
$logo['img'] = '/img/landing2/customer-logos/'.$logo['img'];
}
return $logos;
}
#[Route(path: '/solutions/b2c', name: 'solution_b2c', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'monthly'])]
public function solutionB2c(): Response
{
return $this->render('solutions/b2c.twig');
}
#[Route(path: '/solutions/healthcare', name: 'solution_healthcare', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'monthly'])]
public function solutionHealthcare(): Response
{
return $this->render('solutions/healthcare.twig', [
'logos' => $this->getCustomerLogosHealthcare(),
'badges' => $this->getAwardsBadges(),
]);
}
private function getCustomerLogosHealthcare(): array
{
$logos = self::$customerLogosHealthcare;
$logos = array_slice($logos, 0, 8);
foreach ($logos as &$logo) {
$logo['img'] = '/img/landing2/customer-logos/'.$logo['img'];
}
return $logos;
}
#[Route(path: '/solutions/insurance', name: 'solution_insurance', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'monthly'])]
public function solutionInsurance(): Response
{
return $this->render('solutions/insurance.twig', [
'logos' => $this->getCustomerLogosInsurance(),
'badges' => $this->getAwardsBadges(),
]);
}
private function getCustomerLogosInsurance(): array
{
$logos = self::$customerLogosInsurance;
$logos = array_slice($logos, 0, 4);
foreach ($logos as &$logo) {
$logo['img'] = '/img/landing2/customer-logos/'.$logo['img'];
}
return $logos;
}
#[Route(path: '/solutions/legal', name: 'solution_legal', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'monthly'])]
public function solutionLegal(): Response
{
return $this->render('solutions/legal.twig', [
'logos' => $this->getCustomerLogos(),
'badges' => $this->getAwardsBadges(),
]);
}
#[Route(path: '/solutions/technology', name: 'solution_technology', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'monthly'])]
public function solutionTechnology(): Response
{
return $this->render('solutions/technology.twig', ['logos' => $this->getCustomerLogosTech()]);
}
#[Route(path: '/solutions/utilities', name: 'solution_utilities', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.7', 'sitemap_changefreq' => 'monthly'])]
public function solutionUtilities(): Response
{
return $this->render('solutions/utilities.twig', [
'logos' => $this->getCustomerLogos(),
'badges' => $this->getAwardsBadges(),
]);
}
#[Route(path: '/terms', name: 'terms', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.25', 'sitemap_changefreq' => 'monthly'])]
public function terms(): Response
{
return $this->render('terms.twig');
}
#[Route(path: '/terms/enterprise', name: 'enterprise_terms', methods: ['GET'])]
public function enterpriseTerms(): Response
{
return $this->render('terms-enterprise.twig');
}
#[Route(path: '/privacy', name: 'privacy', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.25', 'sitemap_changefreq' => 'monthly'])]
public function privacy(): Response
{
return $this->render('privacy.twig');
}
#[Route(path: '/savings-calculator', name: 'savings_calculator', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.5', 'sitemap_changefreq' => 'monthly'])]
public function savingsCalculator(): Response
{
return $this->render('resources/savings-calculator.twig');
}
#[Route(path: '/contact', name: 'contact', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.5', 'sitemap_changefreq' => 'monthly'])]
public function contact(): Response
{
return $this->render('/contact.twig');
}
#[Route(path: '/faq', name: 'shortcut_faq', methods: ['GET'])]
public function shortcutFaq(): Response
{
return new RedirectResponse('https://docs.invoiced.com/faqs');
}
#[Route(path: '/resellerprogram', name: 'shortcut_reseller_program', methods: ['GET'])]
public function shortcutResellerProgram(): Response
{
return new RedirectResponse('https://info.invoiced.com/reseller');
}
#[Route(path: '/support', name: 'shortcut_support', methods: ['GET'])]
public function shortcutSupport(): Response
{
return new RedirectResponse('/resources/support', 301);
}
#[Route(path: '/resources/support', name: 'support', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.5', 'sitemap_changefreq' => 'monthly'])]
public function support(): Response
{
return $this->render('resources/support.twig');
}
#[Route(path: '/intro-video', name: 'intro_video', methods: ['GET'])]
public function introVideo(): Response
{
return new RedirectResponse('/resources/videos/intro-video', 301);
}
#[Route(path: '/billing', name: 'billing', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.5', 'sitemap_changefreq' => 'monthly'])]
public function billing(): Response
{
return $this->render('product/billing.twig', [
'logos' => $this->getCustomerLogos(),
'badges' => $this->getAwardsBadges(),
]);
}
#[Route(path: '/collections', name: 'collections', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.5', 'sitemap_changefreq' => 'monthly'])]
public function collections(): Response
{
return $this->render('product/collections.twig', [
'logos' => $this->getCustomerLogos(),
'badges' => $this->getAwardsBadges(),
]);
}
#[Route(path: '/ar-intelligence', name: 'analytics', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.5', 'sitemap_changefreq' => 'monthly'])]
public function arIntelligence(): Response
{
return $this->render('product/analytics.twig', [
'logos' => $this->getCustomerLogos(),
'badges' => $this->getAwardsBadges(),
]);
}
#[Route(path: '/payment-acceptance', name: 'payment_acceptance', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.5', 'sitemap_changefreq' => 'monthly'])]
public function paymentAcceptance(): Response
{
return $this->render('product/payment-acceptance.twig', ['badges' => $this->getAwardsBadges()]);
}
#[Route(path: '/amex', name: 'shortcut_amex', methods: ['GET'])]
public function shortcutAmex(): Response
{
return new RedirectResponse('https://info.invoiced.com/amex');
}
#[Route(path: '/mastercard', name: 'shortcut_mastercard', methods: ['GET'])]
public function shortcutMastercard(): Response
{
return new RedirectResponse('https://info.invoiced.com/mastercard');
}
#[Route(path: '/visa', name: 'shortcut_visa', methods: ['GET'])]
public function shortcutVisa(): Response
{
return new RedirectResponse('https://info.invoiced.com/visa');
}
#[Route(path: '/gocardless', name: 'gocardless_landing', methods: ['GET'])]
public function gocardless(): Response
{
return new RedirectResponse('https://docs.invoiced.com/integrations/gocardless', 301);
}
#[Route(path: '/stripe', name: 'stripe_landing', methods: ['GET'])]
public function stripe(): Response
{
return new RedirectResponse('https://docs.invoiced.com/integrations/stripe', 301);
}
#[Route(path: '/intacct', name: 'legacy_intacct_shortcut', methods: ['GET'])]
#[Route(path: '/product/integrations/intacct', name: 'legacy_intacct', methods: ['GET'])]
#[Route(path: '/sageintacct', name: 'intacct_shortcut', methods: ['GET'])]
public function shortcutIntacct(): Response
{
return new RedirectResponse('/product/integrations/sageintacct', 301);
}
#[Route(path: '/product/integrations/sageintacct', name: 'intacct_landing', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.5', 'sitemap_changefreq' => 'monthly'])]
public function intacct(Request $request): Response
{
$request->query->add([
'utm_source' => 'Intacct',
'utm_medium' => 'landing',
'utm_content' => 'visit',
'utm_campaign' => 'partner',
]);
return $this->render('partners/intacct.twig');
}
#[Route(path: '/product/integrations/netsuite', name: 'netsuite_landing', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.5', 'sitemap_changefreq' => 'monthly'])]
public function netsuite(Request $request): Response
{
$request->query->add([
'utm_source' => 'NetSuite',
'utm_medium' => 'landing',
'utm_content' => 'visit',
'utm_campaign' => 'partner',
]);
return $this->render('partners/netsuite.twig');
}
#[Route(path: '/quickbooks', name: 'shortcut_quickbooks', methods: ['GET'])]
public function shortcutQuickBooks(): Response
{
return new RedirectResponse('/product/integrations/quickbooks', 301);
}
#[Route(path: '/product/integrations/quickbooks', name: 'quickbooks_landing', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.5', 'sitemap_changefreq' => 'monthly'])]
public function quickbooks(Request $request): Response
{
$request->query->add([
'utm_source' => 'QuickBooks',
'utm_medium' => 'landing',
'utm_content' => 'visit',
'utm_campaign' => 'partner',
]);
return $this->render('partners/quickbooks.twig', [
'disconnected' => $request->query->get('disconnected'),
]);
}
#[Route(path: '/xero', name: 'shortcut_xero', methods: ['GET'])]
public function shortcutXero(): Response
{
return new RedirectResponse('/product/integrations/xero', 301);
}
#[Route(path: '/product/integrations/xero', name: 'xero_landing', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.5', 'sitemap_changefreq' => 'monthly'])]
public function xero(Request $request): Response
{
$request->query->add([
'utm_source' => 'Xero',
'utm_medium' => 'landing',
'utm_content' => 'visit',
'utm_campaign' => 'partner',
]);
return $this->render('partners/xero.twig');
}
#[Route(path: '/sitemap.xml', name: 'sitemap_xml', methods: ['GET'])]
public function sitemapXml(RouterInterface $router, Request $request, CacheInterface $cache, Blog $blog, Content $content): Response
{
if ($request->query->get('bust_cache')) {
$cache->delete('sitemap_xml');
}
$pages = $cache->get('sitemap_xml', function (ItemInterface $item) use ($router, $request, $blog, $content) {
$item->expiresAfter(86400); // rebuild sitemap.xml once per day
return $this->buildSitemapPages($router, $request, $blog, $content);
});
// build the sitemap.xml file using Twig
$response = new Response('', 200, ['Content-Type' => 'text/xml']);
return $this->render('sitemap.xml.twig', [
'pages' => $pages,
], $response);
}
private function buildSitemapPages(RouterInterface $router, Request $request, Blog $blog, Content $content): array
{
$pages = [];
// add the static routes
foreach ($router->getRouteCollection()->all() as $route) {
if (!$route->getOption('in_sitemap')) {
continue;
}
$pages[] = [
'url' => $request->getUriForPath($route->getPath()),
'changefreq' => $route->getOption('sitemap_changefreq') ?? 'monthly',
'priority' => $route->getOption('sitemap_priority') ?? 0.7,
];
}
// add blog routes
foreach ($blog->getPosts(-1) as $post) {
$pages[] = [
'url' => $this->generateUrl('blog_post', ['id' => $post['slug']], UrlGeneratorInterface::ABSOLUTE_URL),
'lastmod' => date('Y-m-d', strtotime($post['updated'])),
];
}
foreach ($blog->getAuthors() as $author) {
$pages[] = [
'url' => $this->generateUrl('blog_author', ['author' => $author['slug']], UrlGeneratorInterface::ABSOLUTE_URL),
'changefreq' => 'daily',
'priority' => 0.4,
];
}
foreach ($blog->getTags() as $tag) {
$pages[] = [
'url' => $this->generateUrl('blog_category', ['tag' => $tag['slug']], UrlGeneratorInterface::ABSOLUTE_URL),
'changefreq' => 'daily',
'priority' => 0.4,
];
}
// add case study routes
foreach ($content->getCaseStudies() as $caseStudy) {
$pages[] = [
'url' => $this->generateUrl('case_study', ['id' => $caseStudy['slug']], UrlGeneratorInterface::ABSOLUTE_URL),
'changefreq' => 'monthly',
'priority' => 0.6,
];
}
// add video routes
foreach ($content->getVideos() as $video) {
$pages[] = [
'url' => $video['link'] ?? $this->generateUrl('video', ['id' => $video['slug']], UrlGeneratorInterface::ABSOLUTE_URL),
'changefreq' => 'weekly',
'priority' => 0.6,
];
}
// add webinar routes
foreach ($content->getWebinars() as $webinar) {
$pages[] = [
'url' => $this->generateUrl('webinar', ['id' => $webinar['slug']], UrlGeneratorInterface::ABSOLUTE_URL),
'changefreq' => 'weekly',
'priority' => 0.6,
];
}
// add white paper routes
foreach ($content->getWhitePapers() as $whitePaper) {
$pages[] = [
'url' => $whitePaper['link'] ?? $this->generateUrl('white_paper', ['id' => $whitePaper['slug']], UrlGeneratorInterface::ABSOLUTE_URL),
'changefreq' => 'weekly',
'priority' => 0.6,
];
}
// add the documentation routes
$pages[] = [
'url' => $request->getUriForPath('/resources/docs/api'),
'changefreq' => 'weekly',
'priority' => 0.7,
];
return $pages;
}
#[Route(path: '/schedule-demo', name: 'schedule_demo', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.8', 'sitemap_changefreq' => 'monthly'])]
public function scheduleDemo(Request $request): Response
{
$prefill = [];
if ($params = $request->cookies->get('utm_params')) {
$prefill = json_decode($params, true);
if (!is_array($prefill)) {
$prefill = [];
}
}
// Do not cache demo booking pages
$response = new Response();
$response->headers->set('Cache-Control', 'no-cache, no-store, must-revalidate');
$response->headers->set('Pragma', 'no-cache');
$response->headers->set('Expires', '0');
return $this->render('demo-1.twig', [
'prefillParams' => $prefill,
], $response);
}
#[Route(path: '/demo/2', name: 'schedule_demo_2', methods: ['GET'])]
public function scheduleDemo2(Request $request): Response
{
// Do not cache demo booking pages
$response = new Response();
$response->headers->set('Cache-Control', 'no-cache, no-store, must-revalidate');
$response->headers->set('Pragma', 'no-cache');
$response->headers->set('Expires', '0');
if ($request->cookies->has('booked_demo')) {
$response->setContent('Thank you for scheduling a demo!');
return $response;
}
// validate the email domain has an MX record
$emailParts = explode('@', (string) $request->query->get('email'));
$emailDomain = '';
if (2 == count($emailParts)) {
[, $emailDomain] = $emailParts;
}
if (!$emailDomain || !checkdnsrr($emailDomain.'.', 'MX')) {
$response->setContent('Could not validate email address');
return $response;
}
return $this->render('demo-2.twig', [], $response);
}
#[Route(path: '/demo/thanks', name: 'schedule_demo_thanks', methods: ['GET'])]
public function demoThanks(): Response
{
// Do not cache demo booking pages
$response = new Response();
$response->headers->set('Cache-Control', 'no-cache, no-store, must-revalidate');
$response->headers->set('Pragma', 'no-cache');
$response->headers->set('Expires', '0');
// Visitors can only book one demo per month
$response->headers->setCookie(
new Cookie(
'booked_demo',
'1',
strtotime('+30 days'),
)
);
return $this->render('demo-thanks.twig', [], $response);
}
#[Route(path: '/download', name: 'download', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => '0.5', 'sitemap_changefreq' => 'monthly'])]
public function download(): Response
{
return $this->render('download.twig');
}
#[Route(path: '/guides/ultimate-guide-automating-accounts-receivable-process', name: 'ultimate_guide_automating_accounts_receivable_process', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => 1, 'sitemap_changefreq' => 'weekly'])]
public function ultimateGuideAutomatingAccountsReceivableProcess(): Response
{
return $this->render('landing-page-ar-automation.twig', ['badges' => $this->getAwardsBadges()]);
}
#[Route(path: '/guides/guide-automating-accounts-receivable-law-firms', name: 'guide_automating_accounts_receivable_law_firms', methods: ['GET'], options: ['in_sitemap' => true, 'sitemap_priority' => 1, 'sitemap_changefreq' => 'weekly'])]
public function guideAutomatingAccountsReceivableLawFirms(): Response
{
return $this->render('landing-page-ar-automation-law.twig', ['badges' => $this->getAwardsBadges()]);
}
}