Git Product home page Git Product logo

django-inlinecss's Introduction

Build Status

About

Inlining CSS is necessary for email generation and sending but is currently a surprisingly large hassle.

This library aims to make it a breeze in the Django template language.

Usage

Step 1: Dependencies

  • BeautifulSoup
  • cssutils
  • Python 3.8+
  • Django 3.2+

Step 2: Install django_inlinecss

Add django_inlinecss to your settings.py:

INSTALLED_APPS = (
        'django.contrib.auth',
        'django.contrib.webdesign',
        'django.contrib.contenttypes',
        '...',
        '...',
        '...',
        'django_inlinecss')

Step 3: Use the templatetag

  1. Place your CSS file somewhere staticfiles can find it
  2. Create your template:
{% load inlinecss %}
{% inlinecss "css/extra-padding.css" %}
    <html>
        <body>
            <div class='lots-o-padding'>
                Something in need of styling.
            </div>
        </body>
    </html>
{% endinlinecss %}

Step 4: Prepare to be Wowed

<html>
    <body>
        <div style="padding-left: 10px; padding-right: 10px; padding-top: 10px;" class="lots-o-padding">
            Something in need of styling.
        </div>
    </body>
</html>

Acknowledgements

Thanks to Tanner Netterville for his efforts on Pynliner.

Thanks to Thomas Yip for his unit tests on the soupselect module. These tests helped on getting the core CSS2 selectors to work.

License

MIT license. See LICENSE.md for more detail.

django-inlinecss's People

Contributors

cirotix avatar damacisaac avatar facundojmaero avatar fjsj avatar jpnauta avatar jvenberg avatar keithhackbarth avatar melinath avatar philipkimmey avatar thomasyip avatar vil-s avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-inlinecss's Issues

Fails with remote backends (eg S3)

We use django-storages and S3/Cloudfront for our static resources due to the direct open call in https://github.com/roverdotcom/django-inlinecss/blob/master/django_inlinecss/templatetags/inlinecss.py#L30

File "django/template/base.py", line 830, in render
bit = self.render_node(node, context)
File "django/template/base.py", line 844, in render_node
return node.render(context)
File "django_inlinecss/templatetags/inlinecss.py", line 28, in render
expanded_path = staticfiles_storage.path(path)
File "django/core/files/storage.py", line 85, in path
raise NotImplementedError("This backend doesn't support absolute paths.")

Using staticfiles_storage.open (https://docs.djangoproject.com/en/dev/ref/files/storage/#django.core.files.storage.Storage.open) rather than path() + open is a possible solution

PLEASE NOTE: Project is abandoned? Please transfer to Jazzband.

Firstly thank you for contributing such a useful library πŸ‘

I understand the pressures of ensuring maintenance of open source when other priorities in life can take precedence.

Jazzband is a community to help maintain open source projects and a lot of useful 3rd party Django apps have been transferred there. Please consider the move so that your loyal users can continue to depend on the "official" package moving forward ☺️

Improve Rendering Performance

Currently inlining takes n * m time where n=number of elements in the page & m=number of CSS selectors to operate on.

It should be possible to do the work in `n`` time, though I suppose the potential efficiency gains are dependent on how BeautifulSoup is implemented internally.

(CSS inlining is a relatively expensive operation at this point.)

RichTextFields are not getting css

I have an email I'm sending which includes some django-ckeditor RichTextFields.

My file is of the form

{% load static css_inline thumbnail cms_tags scoring_tags inlinecss %}
<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    ...
</head>
{% inlinecss 'dist/css/shortlist.css' %}
<body>
    <table align="center">
        <tbody>
            <tr>
                <td  class="shortlist">
                    <a href="some_url" class="shortlist"><img id="site-logo" src="path_to_image.png" alt="alt text" width="180px"></a>
                </td>
            </tr>
        </tbody>
    </table>
    <table width="591" border="0" align="center" cellpadding="6" cellspacing="3">
        <tbody>
            <tr>
                <td  class="shortlist" style="padding:10px 0px 0px 5px">
                    <span class="shortlist">
                            Dear {{ object.user.first_name }},
                    </span>
                </td>
            </tr>
            <tr>
                <td  class="shortlist" style="padding-bottom:15px;" >
                    <p class="shortlist">{{ object.auto_intro|safe }}</p>
                    {{ object.intro_email_text|safe }}
                </td>
            </tr>
    </table>
</body>
{% endinlinecss %}
</html>

object.intro_email_text is a RichTextField and no style information appears in the fields. object.auto_intro| is a simple text string

The css file is just this

body, p, td, span {
    font-size:12px;
    font-family: Arial, Helvetica, sans-serif;
    line-height: 1.6;
    color: #000000
}

.shortlist {
    font-family: Arial, Helvetica, sans-serif;
    font-size:12px;
}

.btn-email {
    display: block;
    text-decoration: none;
    background-color: #FF7800;
    color: white;
    cursor: pointer;
    line-height: 50px;
    text-align: center;
    margin: 0px;
    height: 50px;
    padding: 0px 33px;
    border-radius: 5px;
    width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-weight: bold;
}

Is there a way to get the style into the RichTextFields?

Breaking locally on finders.find

This bit of code is breaking locally

        if settings.DEBUG:
            expanded_path = finders.find(path)
        else:
            expanded_path = staticfiles_storage.path(path)

This is returning an empty string.

expanded_path = finders.find(path)

When debug is set to False, it works.

Can we release new version `0.4.0`?

Hi dear maintainers,

I saw that there were a lot of commits, migrationg to GHA, adding support to Django4, flake8, isort etc... Could we release the new version to PyPi as a lot of people are blocked to upgrade to Django4 due to this library.

Best regards,
Sergei

django-inlinecss crashes when DEBUG = False

There's some broken behaviour here that only runs when DEBUG = False.

The condition itself is super dangerous: I only picked this up on CI, but many would have only picked it up on production.

It seems that when not running debug mode:

  • collectstatic is expected to have run. This isn't usually the case when testing/CI/etc.
  • It is assumed that static files are in the local filesystem. This is very often not the case (eg: when using a CDN).

Line 30 crashes because a file is missing. The only way around it it by setting the static storage to a local filesystem storage, and running collectstatic. The former is not viable in production, and I'd rather avoid the latter when running tests locally.

I really don't see any reason to fork depending on the DEBUG setting. It's probably best to always use the staticfiles finder.

An alternative fix, is to actually open the file (rather that extract it's path and assume it's a local path), but this still means collectstatic has to be run on non-production environments.

use of static files is inconsistent

When loading a CSS file inlinecss appears to only look within the static directory within the web root, ie:

{% inlinecss "account/css/Email-Styles.css" %}

is only "seen" here:

/webroot/static/account/css/Email-Styles.css

or as an OS path:

/Library/Webserver/Documents/static/account/css/Email-Styles.css

However the static will find the files elsewhere, for example, in the static directories of loaded apps:

/Library/Webserver/Documents/mydemoapp/static/account/css/Email-Styles.css

and such files are served as a web request:

http://127.0.0.1:8080/static/account/css/Email-Styles.css

But inlinecss will not load files from the static directory of installed_apps.

django-inlinecss-redux now points here

Mostly FYI, feel free to close this after reading it :)

In 2017 I needed a small bug fixed, but this project wasn't active. At the time, I made a fork (django-inlinecss-redux) and pushed that to PyPI.

I see now that this came back to life at some point, and my issue was fixed here too. I've therefore archived django-inlinecss-redux, and added a big sign pointing people here (I don't think anyone else used it anyway).

If you ever do need a hand with maintenance or addressing the smaller issue, do let me know, happy to help. I'm noticing a few issues still open which have been addresses already and are safe to close.

Cheers, and thanks for maintaining this.

CSS Overrides are not working

Hi, great project!

i've been trying it out and works great except for css overrides.

if i have a css which defines the same attribute for a selector, then it takes the first one instead of the last definition

ex:

<a class="button red">HI</a>

.button {
   padding:5px;
   background-color: black;
}

.red {
   background-color: red;
}

the final color of the link should be red, but the css inliner is choosing black as the final bgcolor.

Escapes don't work. Adding even a single escaped forward slash in a class basically just crashes the whole thing.

image
This example above works perfectly.

image
Adding a class name like this in the css that is loaded by inlinecss causes an error. I don't have the time to look into code but I'm guessing it is either replaced by a '\\' (lol I first used double backslashes and even GitHub's editor escaped those into a single blackslash. Edited it to four backslashes.) by python (autoescape of python string) or python string reads as just '/', essentially fulfilling the purpose of the escape at an earlier stage then where it was supposed to be used.

image
This is the error I get

Handle Greater Selector Depth

Currently the test_immediate_child_with_additional_child_selector fails because the selector tests are run against the MATCHED element, rather than the intermediate layer.

The CSS used in this test is:

.wrapper > .header input { ... }

Although it will initially match on input, once it sees the > operator it will check for an immediate parent of input with the wrapper class, instead of a parent of input with class header that has an immediate parent wrapper.

Passing the failing test should be sufficient to prove this is resolved.

FAIL: test_immediate_child_with_additional_child_selector (django_inlinecss.tests.pynliner_tests.ComplexSelectors)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/philip/workspace/django-inlinecss/django_inlinecss/tests/pynliner_tests.py", line 468, in test_immediate_child_with_additional_child_selector
    self.assertEqual(output, expected)
AssertionError: u'<div class="wrapper"><div class="header"><input type="text" /></div></div>' != u'<div class="wrapper"><div class="header"><input type="text" style="color: red" [truncated]...
- <div class="wrapper"><div class="header"><input type="text" /></div></div>
+ <div class="wrapper"><div class="header"><input type="text" style="color: red" /></div></div>
?                                                            +++++++++++++++++++

pip install fails on Python 3.4.1

The installation fails because BeautifulSoup is not compatible with Python 3.
A quick-fix might be to use BeautifulSoup4 instead, as it should be compatible with Python 2 and Python 3

The install error message is:

pip install django-inlinecss
Downloading/unpacking django-inlinecss
  Downloading django-inlinecss-0.1.1.tar.gz
  Running setup.py (path:/Users/xxx/.virtualenvs/xxx-my-project/build/django-inlinecss/setup.py) egg_info for package django-inlinecss

Requirement already satisfied (use --upgrade to upgrade): Django in /Users/xxx/.virtualenvs/xxx-my-project/lib/python3.4/site-packages (from django-inlinecss)
Requirement already satisfied (use --upgrade to upgrade): cssutils in /Users/xxx/.virtualenvs/xxx-my-project/lib/python3.4/site-packages (from django-inlinecss)
Downloading/unpacking BeautifulSoup (from django-inlinecss)
  Downloading BeautifulSoup-3.2.1.tar.gz
  Running setup.py (path:/Users/xxx/.virtualenvs/xxx-my-project/build/BeautifulSoup/setup.py) egg_info for package BeautifulSoup
    Traceback (most recent call last):
      File "<string>", line 17, in <module>
      File "/Users/xxx/.virtualenvs/xxx-my-project/build/BeautifulSoup/setup.py", line 22
        print "Unit tests have failed!"
                                      ^
    SyntaxError: invalid syntax
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):

  File "<string>", line 17, in <module>

  File "/Users/xxx/.virtualenvs/xxx-my-project/build/BeautifulSoup/setup.py", line 22

    print "Unit tests have failed!"

                                  ^

SyntaxError: invalid syntax

----------------------------------------
Cleaning up...
Command python setup.py egg_info failed with error code 1 in /Users/xxx/.virtualenvs/xxx-my-project/build/BeautifulSoup
Storing debug log for failure in /Users/xxx/.pip/pip.log

Got error unknown url type: '/static/ProjectName/style.css'

1{% load static %}
2{% load inlinecss %}
3{% inlinecss 'ProjectName/style.css' %}
4<html lang="en">
5<head>
6
7</head>
8<body>
...
9</body>
...
10<link rel="stylesheet" href="{% static 'ProjectName/style.css' %}">
11{% endinlinecss %}

so I have a html basically looks like this, while the 10th can load the css normally, but I got a ValueError exception with the value
unknown url type: '/static/ProjectName/style.css' and the exception occur on location: C:\mypathname\Python39\lib\urllib\request.py

anyone can help?

Python 3 support

On Python 3 I get this error:

  File "...python3.5/site-packages/pynliner/__init__.py", line 222, in _get_external_styles
    self.style_string += self._get_url(url)
TypeError: Can't convert 'bytes' object to str implicitly

It's a pynliner problem.
I have already described it here and opened a pull request to fix this but @rennat seem to be too busy to review it.

Is there a way to make django-inlinecss work with Python 3?

urllib HTTP Error 400

Hello, there

I am getting following error when send email while using django-inlinecss.

ERROR
<class 'urllib.error.HTTPError'>
HTTP Error 400: Bad Request

Can you please let me know reason?

Readme documentation should include INLINECSS_CSS_LOADER setting option details

I ran into the problem with the {% inlinecss %} tag being unable to open CSS files in production test environments. The cause was that the standard CSS loader is looking in the collectstatic destination directory. In some setups, the collectstatic directory is only accessible via http calls via the webserver, and not to the WSGI Python workers that run the Django code. Really since the CSS is being read by the Django Python workers in {% inlinecss %}, the CSS should be the source static directories rather than the collectstatic dir.

I was able to fix this by looking at the source code and finding you'd already written the class I was otherwise going to write (django_inlinecss.css_loaders.StaticfilesFinderCSSLoader)!

I spotted the conf getattr(settings, INLINE_CSS_LOADER) line, so fixed it all with:
INLINECSS_CSS_LOADER = "django_inlinecss.css_loaders.StaticfilesFinderCSSLoader"

I think this should be included in the Readme so users who struggle with IOerrors in production / tests know how to change the behaviour.


Relates to #54, but this is specifically for the Readme.

TypeError must be str, not bytes

You do a nice job with django-inlinecss.

I have a question, when I try to send the email with django-inlinecss it appears the following error: "TypeError must be str, not bytes" in line "{% inlinecss" demo / demo.css "%}". How can I fix this error? Thank you very much and sorry to bother you.

ImportError: No module named django_inlinecss

Hi there,

I tried installing the library as per the instructions, but am getting the error shown below. I noticed there is a Stackoverflow post on this already, but it seems to not have been resolved.

Any idea how to fix this? I'm super excited to use this library.

Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/django/utils/autoreload.py", line 93, in wrapper
    fn(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/core/management/commands/runserver.py", line 102, in inner_run
    self.validate(display_num_errors=True)
  File "/usr/local/lib/python2.7/dist-packages/django/core/management/base.py", line 310, in validate
    num_errors = get_validation_errors(s, app)
  File "/usr/local/lib/python2.7/dist-packages/django/core/management/validation.py", line 34, in get_validation_errors
    for (app_name, error) in get_app_errors().items():
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/loading.py", line 196, in get_app_errors
    self._populate()
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/loading.py", line 75, in _populate
    self.load_app(app_name, True)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/loading.py", line 97, in load_app
    app_module = import_module(app_name)
  File "/usr/local/lib/python2.7/dist-packages/django/utils/importlib.py", line 40, in import_module
    __import__(name)
ImportError: No module named django_inlinecss

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.