mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-09 20:52:56 +01:00
Merge pull request #588 from joshuadwire/custom-css
Add custom CSS support
This commit is contained in:
commit
56330c1281
@ -152,6 +152,8 @@ class AccountController extends BaseController
|
||||
return View::make('accounts.import_export', ['title' => trans('texts.import_export')]);
|
||||
} elseif ($section == ACCOUNT_INVOICE_DESIGN || $section == ACCOUNT_CUSTOMIZE_DESIGN) {
|
||||
return self::showInvoiceDesign($section);
|
||||
} elseif ($section == ACCOUNT_CLIENT_VIEW_STYLING) {
|
||||
return self::showClientViewStyling();
|
||||
} elseif ($section === ACCOUNT_TEMPLATES_AND_REMINDERS) {
|
||||
return self::showTemplates();
|
||||
} elseif ($section === ACCOUNT_PRODUCTS) {
|
||||
@ -367,6 +369,29 @@ class AccountController extends BaseController
|
||||
return View::make("accounts.{$section}", $data);
|
||||
}
|
||||
|
||||
private function showClientViewStyling()
|
||||
{
|
||||
$account = Auth::user()->account->load('country');
|
||||
$css = $account->client_view_css ? $account->client_view_css : '';
|
||||
|
||||
if(Utils::isNinja() && $css){
|
||||
// Unescape the CSS for display purposes
|
||||
$css = str_replace(
|
||||
array('\3C ', '\3E ', '\26 '),
|
||||
array('<', '>', '&'),
|
||||
$css
|
||||
);
|
||||
}
|
||||
|
||||
$data = [
|
||||
'client_view_css' => $css,
|
||||
'title' => trans("texts.client_view_styling"),
|
||||
'section' => ACCOUNT_CLIENT_VIEW_STYLING
|
||||
];
|
||||
|
||||
return View::make("accounts.client_view_styling", $data);
|
||||
}
|
||||
|
||||
private function showTemplates()
|
||||
{
|
||||
$account = Auth::user()->account->load('country');
|
||||
@ -408,6 +433,8 @@ class AccountController extends BaseController
|
||||
return AccountController::saveInvoiceDesign();
|
||||
} elseif ($section === ACCOUNT_CUSTOMIZE_DESIGN) {
|
||||
return AccountController::saveCustomizeDesign();
|
||||
} elseif ($section === ACCOUNT_CLIENT_VIEW_STYLING) {
|
||||
return AccountController::saveClientViewStyling();
|
||||
} elseif ($section === ACCOUNT_TEMPLATES_AND_REMINDERS) {
|
||||
return AccountController::saveEmailTemplates();
|
||||
} elseif ($section === ACCOUNT_PRODUCTS) {
|
||||
@ -430,6 +457,53 @@ class AccountController extends BaseController
|
||||
return Redirect::to('settings/' . ACCOUNT_CUSTOMIZE_DESIGN);
|
||||
}
|
||||
|
||||
private function saveClientViewStyling() {
|
||||
// Only allowed for pro Invoice Ninja users or white labeled self-hosted users
|
||||
if ((Utils::isNinja() && Auth::user()->account->isPro()) || Auth::user()->account->isWhiteLabel()) {
|
||||
|
||||
$input_css = Input::get('client_view_css');
|
||||
if(Utils::isNinja()){
|
||||
// Allow referencing the body element
|
||||
$input_css = preg_replace('/(?<![a-z0-9\-\_\#\.])body(?![a-z0-9\-\_])/i', '.body', $input_css);
|
||||
|
||||
//
|
||||
// Inspired by http://stackoverflow.com/a/5209050/1721527, dleavitt <https://stackoverflow.com/users/362110/dleavitt>
|
||||
//
|
||||
|
||||
// Create a new configuration object
|
||||
$config = \HTMLPurifier_Config::createDefault();
|
||||
$config->set('Filter.ExtractStyleBlocks', true);
|
||||
$config->set('CSS.AllowImportant', true);
|
||||
$config->set('CSS.AllowTricky', true);
|
||||
$config->set('CSS.Trusted', true);
|
||||
|
||||
// Create a new purifier instance
|
||||
$purifier = new \HTMLPurifier($config);
|
||||
|
||||
// Wrap our CSS in style tags and pass to purifier.
|
||||
// we're not actually interested in the html response though
|
||||
$html = $purifier->purify('<style>'.$input_css.'</style>');
|
||||
|
||||
// The "style" blocks are stored seperately
|
||||
$output_css = $purifier->context->get('StyleBlocks');
|
||||
|
||||
// Get the first style block
|
||||
$sanitized_css = $output_css[0];
|
||||
}
|
||||
else{
|
||||
$sanitized_css = $input_css;
|
||||
}
|
||||
|
||||
$account = Auth::user()->account;
|
||||
$account->client_view_css = $sanitized_css;
|
||||
$account->save();
|
||||
|
||||
Session::flash('message', trans('texts.updated_settings'));
|
||||
}
|
||||
|
||||
return Redirect::to('settings/' . ACCOUNT_CLIENT_VIEW_STYLING);
|
||||
}
|
||||
|
||||
private function saveEmailTemplates()
|
||||
{
|
||||
if (Auth::user()->account->isPro()) {
|
||||
|
@ -103,6 +103,7 @@ class InvoiceController extends BaseController
|
||||
return response()->view('error', [
|
||||
'error' => trans('texts.invoice_not_found'),
|
||||
'hideHeader' => true,
|
||||
'clientViewCSS' => $account->clientViewCSS(),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -154,6 +155,7 @@ class InvoiceController extends BaseController
|
||||
'showBreadcrumbs' => false,
|
||||
'hideLogo' => $account->isWhiteLabel(),
|
||||
'hideHeader' => $account->isNinjaAccount(),
|
||||
'clientViewCSS' => $account->clientViewCSS(),
|
||||
'invoice' => $invoice->hidePrivateFields(),
|
||||
'invitation' => $invitation,
|
||||
'invoiceLabels' => $account->getInvoiceLabels(),
|
||||
|
@ -177,6 +177,7 @@ class PaymentController extends BaseController
|
||||
'account' => $client->account,
|
||||
'hideLogo' => $account->isWhiteLabel(),
|
||||
'hideHeader' => $account->isNinjaAccount(),
|
||||
'clientViewCSS' => $account->clientViewCSS(),
|
||||
'showAddress' => $accountGateway->show_address,
|
||||
];
|
||||
|
||||
|
@ -37,6 +37,7 @@ class PublicClientController extends BaseController
|
||||
'account' => $account,
|
||||
'client' => $client,
|
||||
'hideLogo' => $account->isWhiteLabel(),
|
||||
'clientViewCSS' => $account->clientViewCSS(),
|
||||
];
|
||||
|
||||
return response()->view('invited.dashboard', $data);
|
||||
@ -81,6 +82,7 @@ class PublicClientController extends BaseController
|
||||
$data = [
|
||||
'color' => $color,
|
||||
'hideLogo' => $account->isWhiteLabel(),
|
||||
'clientViewCSS' => $account->clientViewCSS(),
|
||||
'title' => trans('texts.invoices'),
|
||||
'entityType' => ENTITY_INVOICE,
|
||||
'columns' => Utils::trans(['invoice_number', 'invoice_date', 'invoice_total', 'balance_due', 'due_date']),
|
||||
@ -110,6 +112,7 @@ class PublicClientController extends BaseController
|
||||
$data = [
|
||||
'color' => $color,
|
||||
'hideLogo' => $account->isWhiteLabel(),
|
||||
'clientViewCSS' => $account->clientViewCSS(),
|
||||
'entityType' => ENTITY_PAYMENT,
|
||||
'title' => trans('texts.payments'),
|
||||
'columns' => Utils::trans(['invoice', 'transaction_reference', 'method', 'payment_amount', 'payment_date'])
|
||||
@ -145,6 +148,7 @@ class PublicClientController extends BaseController
|
||||
$data = [
|
||||
'color' => $color,
|
||||
'hideLogo' => $account->isWhiteLabel(),
|
||||
'clientViewCSS' => $account->clientViewCSS(),
|
||||
'title' => trans('texts.quotes'),
|
||||
'entityType' => ENTITY_QUOTE,
|
||||
'columns' => Utils::trans(['quote_number', 'quote_date', 'quote_total', 'due_date']),
|
||||
@ -168,6 +172,7 @@ class PublicClientController extends BaseController
|
||||
return response()->view('error', [
|
||||
'error' => trans('texts.invoice_not_found'),
|
||||
'hideHeader' => true,
|
||||
'clientViewCSS' => $account->clientViewCSS(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -278,6 +278,7 @@ if (!defined('CONTACT_EMAIL')) {
|
||||
define('ACCOUNT_ADVANCED_SETTINGS', 'advanced_settings');
|
||||
define('ACCOUNT_INVOICE_SETTINGS', 'invoice_settings');
|
||||
define('ACCOUNT_INVOICE_DESIGN', 'invoice_design');
|
||||
define('ACCOUNT_CLIENT_VIEW_STYLING', 'client_view_styling');
|
||||
define('ACCOUNT_EMAIL_SETTINGS', 'email_settings');
|
||||
define('ACCOUNT_CHARTS_AND_REPORTS', 'charts_and_reports');
|
||||
define('ACCOUNT_USER_MANAGEMENT', 'user_management');
|
||||
|
@ -35,6 +35,7 @@ class Account extends Eloquent
|
||||
public static $advancedSettings = [
|
||||
ACCOUNT_INVOICE_SETTINGS,
|
||||
ACCOUNT_INVOICE_DESIGN,
|
||||
ACCOUNT_CLIENT_VIEW_STYLING,
|
||||
ACCOUNT_EMAIL_SETTINGS,
|
||||
ACCOUNT_TEMPLATES_AND_REMINDERS,
|
||||
ACCOUNT_CHARTS_AND_REPORTS,
|
||||
@ -873,6 +874,14 @@ class Account extends Eloquent
|
||||
{
|
||||
return $this->isPro() && $this->pdf_email_attachment;
|
||||
}
|
||||
|
||||
public function clientViewCSS(){
|
||||
if (($this->isNinjaAccount() && $this->isPro()) || $this->isWhiteLabel()) {
|
||||
return $this->client_view_css;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Account::updated(function ($account) {
|
||||
|
@ -72,6 +72,7 @@ class AccountTransformer extends EntityTransformer
|
||||
'invoice_taxes' => (bool) $account->invoice_taxes,
|
||||
'invoice_item_taxes' => (bool) $account->invoice_item_taxes,
|
||||
'invoice_design_id' => (int) $account->invoice_design_id,
|
||||
'client_view_css' => (string) $account->client_view_css,
|
||||
'work_phone' => $account->work_phone,
|
||||
'work_email' => $account->work_email,
|
||||
'language_id' => (int) $account->language_id,
|
||||
|
@ -63,7 +63,9 @@
|
||||
"collizo4sky/omnipay-wepay": "~1.0",
|
||||
"laracasts/presenter": "dev-master",
|
||||
"jlapp/swaggervel": "master-dev",
|
||||
"maatwebsite/excel": "~2.0"
|
||||
"maatwebsite/excel": "~2.0",
|
||||
"ezyang/htmlpurifier": "~v4.7",
|
||||
"cerdic/css-tidy": "~v1.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.0",
|
||||
|
81
composer.lock
generated
81
composer.lock
generated
@ -4,8 +4,8 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "26f955f9f0cfc85f6ec046f8600b04c7",
|
||||
"content-hash": "8c51afe64fbaf07534ceaedaddf66206",
|
||||
"hash": "f3b07ee34ed5086cd684a7207fd3617e",
|
||||
"content-hash": "7305e2f5d6864894aeb23ac91f3e1fe7",
|
||||
"packages": [
|
||||
{
|
||||
"name": "agmscode/omnipay-agms",
|
||||
@ -496,6 +496,39 @@
|
||||
],
|
||||
"time": "2015-06-05 14:50:44"
|
||||
},
|
||||
{
|
||||
"name": "cerdic/css-tidy",
|
||||
"version": "v1.5.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Cerdic/CSSTidy.git",
|
||||
"reference": "b0c1bda3be5a35da44ba1ac28cc61c67d2ada465"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Cerdic/CSSTidy/zipball/b0c1bda3be5a35da44ba1ac28cc61c67d2ada465",
|
||||
"reference": "b0c1bda3be5a35da44ba1ac28cc61c67d2ada465",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"."
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-2.1"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Cédric MORIN",
|
||||
"email": "changeme@mailinator.com"
|
||||
}
|
||||
],
|
||||
"description": "CSSTidy is a CSS minifier",
|
||||
"time": "2015-11-28 21:47:43"
|
||||
},
|
||||
{
|
||||
"name": "chumper/datatable",
|
||||
"version": "dev-develop",
|
||||
@ -1695,6 +1728,50 @@
|
||||
],
|
||||
"time": "2015-06-05 13:57:26"
|
||||
},
|
||||
{
|
||||
"name": "ezyang/htmlpurifier",
|
||||
"version": "v4.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ezyang/htmlpurifier.git",
|
||||
"reference": "ae1828d955112356f7677c465f94f7deb7d27a40"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/ae1828d955112356f7677c465f94f7deb7d27a40",
|
||||
"reference": "ae1828d955112356f7677c465f94f7deb7d27a40",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.2"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"HTMLPurifier": "library/"
|
||||
},
|
||||
"files": [
|
||||
"library/HTMLPurifier.composer.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Edward Z. Yang",
|
||||
"email": "admin@htmlpurifier.org",
|
||||
"homepage": "http://ezyang.com"
|
||||
}
|
||||
],
|
||||
"description": "Standards compliant HTML filter written in PHP",
|
||||
"homepage": "http://htmlpurifier.org/",
|
||||
"keywords": [
|
||||
"html"
|
||||
],
|
||||
"time": "2015-08-05 01:03:42"
|
||||
},
|
||||
{
|
||||
"name": "fotografde/omnipay-checkoutcom",
|
||||
"version": "2.0",
|
||||
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddClientViewCss extends Migration {
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('accounts', function ($table) {
|
||||
$table->text('client_view_css')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('accounts', function ($table) {
|
||||
$table->dropColumn('client_view_css');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -60,6 +60,7 @@ There are two options:
|
||||
* [Troels Liebe Bentsen](https://github.com/tlbdk)
|
||||
* [Jeramy Simpson](https://github.com/JeramyMywork) - [MyWork](https://www.mywork.com.au)
|
||||
* [Sigitas Limontas](https://lt.linkedin.com/in/sigitaslimontas)
|
||||
* [Joshua Dwire](https://github.com/joshuadwire) - [Some Techie](https://www.sometechie.com)
|
||||
|
||||
### Frameworks/Libraries
|
||||
* [laravel/laravel](https://github.com/laravel/laravel) - A PHP Framework For Web Artisans
|
||||
@ -93,4 +94,6 @@ There are two options:
|
||||
* [simshaun/recurr](https://github.com/simshaun/recurr) - PHP library for working with recurrence rules
|
||||
* [quilljs/quill](https://github.com/quilljs/quill/) - A cross browser rich text editor with an API
|
||||
* [Maatwebsite/Laravel-Excel](https://github.com/Maatwebsite/Laravel-Excel) - An eloquent way of importing and exporting Excel and CSV files for Laravel
|
||||
* [thephpleague/fractal](https://github.com/thephpleague/fractal) - Output complex, flexible, AJAX/RESTful data structures
|
||||
* [thephpleague/fractal](https://github.com/thephpleague/fractal) - Output complex, flexible, AJAX/RESTful data structures
|
||||
* [ezyang/htmlpurifier](https://github.com/ezyang/htmlpurifier) - Standards compliant HTML filter written in PHP
|
||||
* [cerdic/css-tidy](https://github.com/Cerdic/CSSTidy) - CSSTidy is a CSS minifier
|
||||
|
@ -203,7 +203,9 @@ return array(
|
||||
'client_will_create' => 'client will be created',
|
||||
'clients_will_create' => 'clients will be created',
|
||||
'email_settings' => 'Email Settings',
|
||||
'client_view_styling' => 'Client View Styling',
|
||||
'pdf_email_attachment' => 'Attach PDFs',
|
||||
'custom_css' => 'Custom CSS',
|
||||
|
||||
//import CSV data pages
|
||||
'import_clients' => 'Import Client Data',
|
||||
@ -474,6 +476,8 @@ return array(
|
||||
'id_number' => 'ID Number',
|
||||
'white_label_link' => 'White label',
|
||||
'white_label_text' => 'Purchase a white label license for $'.WHITE_LABEL_PRICE.' to remove the Invoice Ninja branding from the client portal and help support our project.',
|
||||
'white_label_custom_css' => ':link for $'.WHITE_LABEL_PRICE.' to enable custom styling and help support our project.',
|
||||
'white_label_purchase_link' => 'Purchase a white label license',
|
||||
'white_label_header' => 'White Label',
|
||||
'bought_white_label' => 'Successfully enabled white label license',
|
||||
'white_labeled' => 'White labeled',
|
||||
|
52
resources/views/accounts/client_view_styling.blade.php
Normal file
52
resources/views/accounts/client_view_styling.blade.php
Normal file
@ -0,0 +1,52 @@
|
||||
@extends('header')
|
||||
|
||||
@section('head')
|
||||
@parent
|
||||
|
||||
<link href='https://fonts.googleapis.com/css?family=Roboto+Mono' rel='stylesheet' type='text/css'>
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
@parent
|
||||
|
||||
{!! Former::open_for_files()
|
||||
->addClass('warn-on-exit') !!}
|
||||
|
||||
{!! Former::populateField('client_view_css', $client_view_css) !!}
|
||||
|
||||
@if (!Utils::isNinja() && !Auth::user()->account->isWhiteLabel())
|
||||
<div class="alert alert-warning" style="font-size:larger;">
|
||||
<center>
|
||||
{!! trans('texts.white_label_custom_css', ['link'=>'<a href="#" onclick="$(\'#whiteLabelModal\').modal(\'show\');">'.trans('texts.white_label_purchase_link').'</a>']) !!}
|
||||
</center>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@include('accounts.nav', ['selected' => ACCOUNT_CLIENT_VIEW_STYLING])
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{!! trans('texts.custom_css') !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{!! Former::textarea('client_view_css')
|
||||
->label(trans('texts.custom_css'))
|
||||
->rows(15)
|
||||
->autofocus()
|
||||
->maxlength(60000)
|
||||
->style("min-width:100%;max-width:100%;font-family:'Roboto Mono', 'Lucida Console', Monaco, monospace;font-size:14px;'") !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<center>
|
||||
{!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!}
|
||||
</center>
|
||||
|
||||
{!! Former::close() !!}
|
||||
|
||||
@stop
|
@ -111,7 +111,7 @@
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body class="body">
|
||||
|
||||
@if (isset($_ENV['TAG_MANAGER_KEY']) && $_ENV['TAG_MANAGER_KEY'])
|
||||
<!-- Google Tag Manager -->
|
||||
|
@ -2,6 +2,9 @@
|
||||
|
||||
@section('head')
|
||||
<link href="{{ asset('css/built.public.css') }}?no_cache={{ NINJA_VERSION }}" rel="stylesheet" type="text/css"/>
|
||||
@if (!empty($clientViewCSS))
|
||||
<style>{!! $clientViewCSS !!}</style>
|
||||
@endif
|
||||
@stop
|
||||
|
||||
@section('body')
|
||||
|
Loading…
Reference in New Issue
Block a user