How to import styles from css files into an .ejs template and send it over email? - css

What i want to do:
Send one email with styles;
Have separate files, one for my .ejs template and one .css for styles that correspond to that template.
What the problem is:
If i create separate routes for the styles and insert it into the HTML, the user will need to "authorize" the download of these styles;
If i just import like normal HTML, it will not go through the email.
What am i using:
Express.js
Typescript
EJS
nodemailer
What is my file structure like at the moment:
src
views
index.ejs
style.css
internal_view_group
style.css
internal_template_name
style.css
locale
pt-br.ejs
en-us.ejs
public_view_group
style.css
public_template_name
style.css
locale
pt-br.ejs
en-us.ejs
Ok, if the directory is a little messy or hard to understand, let me break it down for you:
Folder "views" contains all my templates and styles, it also contains my index.ejs, that's what i'll call for when rendering the HTML, this index will then call styles from the group, template and then call the HTML from template, considering the language it uses. It also has a style.css, a global stylesheet that must be used in all templates, regardless of it's group.
Folder "internal_view_group" is a group of templates, for example, we have an internal group, templates used for internal communication, and a public group, used for the general public communication. It also has a default style.css, that is applied to every template inside this group.
Folder "internal_template_name" is a template, it has it's locales inside "locales" folder and a stylesheet that must be applied onto the called locale.
How can i have this level of organization on my code, having separate files and still work, considering the section "The problem is"?

Since you use your index.ejs to call all other templates and styles, you can also directly inject the styles into this index.ejs using the <%- %> tag from EJS.
What this tag does: import a file (usually an .EJS), read it's content and output it unscaped to the HTML.
so what you want to do into the index.js is something similar to this:
<head>
<%-
'<style>'
+ include(`./style.css`)
+ include(`./${template_group}/style.css`)
+ include(`./${template_group}/${template_name}/style.css`)
+ '</style>'
%>
</head>
<body>
<%- include(`./${template_group}/${template_name}/locale/${locale}.ejs`) %>
</body>
This will output all style RAW contents into the code (not a minified version).
The reason for the style tag being inside strings is that VSCode may report an error in your code because of EJS tags inside style sections, with this, only the < at the start of <style> will be reported as "wrong", but with nothing at output terminal telling you.
If you find that the final size of HTML code that you want to send is too large and may become a problem, you can use javascript prototype.String functions after the include, like include('./style.css').replaceAll(' ', ''), to replace all spaces but leave line breaks (not tested)

Related

How to add <style> tag in Vue.js component

I'm on Vue.js v2. I have a CSS stylesheet stored as a string in a variable.
import sitePackCss from '!!raw-loader!sass-loader!../../app/javascript/styles/site.sass';
I need to create a tag from my component.
<style v-html="sitePackCss" />
OR
<style>{sitePackCss}</style>
When I do either of these, I get the following error in the console:
Templates should only be responsible for mapping the state to the UI. Avoid placing tags with side-effects in your templates, such as <style>, as they will not be parsed.
How do I get this tag onto the page?
NOTE: I know this is a hacky, non-preferred way to include styles. This solution will only get used in the context of storybook, where I need to include specific CSS files for specific stories (without storybook/webpack adding them to every story). If I use normal webpack loaders, each tag is added to every story. Importing the styles as a string is the only way I've found to sidestep that behavior.
Try to add the style to the src tag of the style in your SFC :
<style lang="sass" src="../../app/javascript/styles/site.sass">
</style>
This seems to work!
import sitePackCss from '!!raw-loader!sass-loader!../../app/javascript/styles/site.sass';
In template:
<component is="style" type="text/css">${sitePackCss}</component>
Note: the sass files have references to fonts that were not working correctly using this technique. I had to update the staticDirs config to make those paths work. https://storybook.js.org/docs/react/configure/images-and-assets

Use html-webpack-plugin to inject bundle script tags into <asp:Content> tag in legacy .aspx files with master page?

I'm fairly familiar with webpack and the html-webpack-plugin and have used them on a couple of other (SPA) projects. But in this new project I have to convert a legacy multi-page website to use webpack. There is a custom asp.net handler (ashx) that currently bundles (and minifies in prod builds) registered scripts by placing a comma separate list of script names on the query string of the .ashx reference in a script tag: <script src="Script.ashx?i=jquery,jquery-ui,...">.
One of the problems is that almost all the pages use a master page. So, there is no <body> tag to use for the html-webpack-plugin.
If I was dealing with a small number of entries I would have no problem using a few html-webpack-plugin templates to inject the scripts and place the output files in the correct place in the project folder structure. But there are 50 aspx pages in various locations in the project folder structure. So I would very much like to avoid maintaining separate templates for all of those pages.
But given that there no <body> tags in any of these files, how do I inject the scripts into the desired place?
I've built a custom code generator to read all the aspx pages in the project and find the Script.ashx references. It then parses the comma separated query string and generates a companion .js file with one import statement for each of the referenced scripts. These companion .js files will be what are referenced in the webpack "entry" array. So, for instance /home.aspx gets a companion /home-entry.js file. That file is in the webpack config: entry { "home" : "./home-entry", ... }. And the corresponding Script.ashx is commented out in the source aspx page. I'm also code generating the webpack entry array and the html-webpack-plugin references for each entry into the plugins array in the webpack config.
Home.aspx (snippet):
<asp:Content ID="Content6" ContentPlaceHolderID="footerPlaceHolder" runat="server">
<script type="text/javascript" src="/Script.ashx?i=jquery,jquery-migrate,jquery-ui,jquery-watermark,popr,acrobat-detection,pdflinkfix,device,navbar,jqdialoghelper,home&v=<%= "" + MyNameSpace.Scripts.ScriptHelpers.AssemblyVersion %>"></script>
</asp:Content>
Entry example:
"home" : "./home-entry",
Plugins example:
new HtmlWebpackPlugin( {
chunks: ['home-entry'],
alwaysWriteToDisk: true,
filename: "./home.aspx",
inject: 'body', // or what?
chunkSortMode: "dependency",
hash: true
} ),
home-entry.js:
import '/Scripts/jquery-3.3.1.min'
import '/Scripts/jquery-migrate-min'
import '/Scripts/jquery-ui-1.12.1.min'
import '/Scripts/jquery.watermark.min'
import '/Scripts/popr/popr'
import '/Scripts/acrobat_detection'
import '/Scripts/PDFLinkFix'
import '/Scripts/Device'
import '/Scripts/NavBar'
import '/Scripts/jqDialogHelper'
import '/Scripts/Home'
Expected result: The big problem is that I cannot figure out if there is a way to tell html-webpack-plugin to inject into a specific tag. I.e. what I want it to do is find the specific <asp:Content ID="Content6" ContentPlaceHolderID="footerPlaceHolder" runat="server"> tag and inject the script tags into it. Note that there are other <asp:Content> tags that have different ContentPlaceHolderID values. So html-webpack-plugin has to find the one with ContentPlaceHolderID="footerPlaceHolder".
Actual result: I believe with a default html-webpack-plugin options, in the absence of a <body> tag, the plugin will place the scripts at the end of the file. Which will confuse asp.net.
It sounds like the codebase I am working on has a similar setup to yours. I searched for a solution for quite a while to no avail. I ended up writing my own webpack plugin which handles this situation for me now. It defaults to writing to an index.aspx page but you can specify whatever type of page and/or location you'd like it to write to.
You can check it out here. Feel free to install and use if you'd like. Its not super polished or anything but its working well for our setup.
https://github.com/pckessel/MPAInjectionWebpackPlugin

EJS Tags in external css

I'm trying to refactor a nodeJs project and my css is in my .ejs file. I need to put some dynamic data in my css file. I need to have dynamic data in my css, when my css is in my EJS file it's good.
But when I put my css into an external file, nothing happens!
So, how can I inject ejs tag in my external stylesheet file?
This is one of the questions to which one has to ask you - why do you want to do that? You are asking about a solution to a problem that you didn't describe.
By suspicion is that you are trying to use a templating system for something that could be done with a CSS preprocessor like Sass or Less. Particularly Less can be easily used on both the client side and server side. See http://lesscss.org/#client-side-usage
Now, if you really want to use a templating system created for HTML to generate CSS then you will have to take few things into account: You need to store your CSS templates somewhere where your HTML templates are stored, not where you store your static assets like images. Then, your server will have to serve your generated CSS with a correct MIME type. Then, your HTML will have to reference the styles using a URL to your generated CSS that will be generated on every request instead of being served directly from disk.

Integrate CKEditor in Meteor

I'm trying to use CKEditor in a meteor application:
My attemps:
Put CKEditor folder with all the files (js, css, lang, plugins and skins) in the public folder, include the reference to the javascript file (ckeditor.js) in the header and use the appropiate class in textarea elements. Failed because the editor only works if the textarea is in the body (in any template the textarea control remains unmodified).
Put the javascript files (ckeditor.js, config.js, styles.js) in client/lib/compatibility folder and the remaining files in the public folder. This time the application cant locate the files (skins, plugins, ...) because is looking for localhost:3000/client/lib/compatibility/ckeditor/ ...
Has anybody make this integration works before?
I got this working and wanted to post a solution for future visitors. First, you need to put everything from the CKEDITOR build download in the public folder. CKEDITOR comes with all sorts of stuff and references everything based on relative directories.
Your public folder should have a directory named ckeditor it should contain contain the following files and folders:
adapters
lang
plugins
skins
ckeditor.js
config.js
contents.css
styles.js
In your primary layout file reference CKEDITOR like so:
<head>
<script type="text/javascript" src="/ckeditor/ckeditor.js"></script>
<script type="text/javascript" src="/ckeditor/adapters/jquery.js"></script>
</head>
In your template:
<template name="yourTemplate">
<textarea id="content" name="content"></textarea>
</template>
Finally, in the rendered function of your template:
Template.yourTemplate.rendered = function() {
$('#content').ckeditor();
};
Normally, you would say this.$('#content').ckeditor() but that doesn't work because CKEDITOR is in your public folder. As a result, you need to the global reference to the #content element.
Put only the CKEditor files that you would've included in <head> inside a folder in client/lib, i.e. client/lib/ckeditor. That's all you need to do to get them served to the client: there's no need to reference anything in any <head> or anything like that. All .js and .css files that Meteor finds inside client are automatically concatenated and served to the client. This applies to any client-side library, not just CKEditor.
The next thing you need to do is cause CKEditor to be initialized on the pages that use it. Say you have a template called edit with a textarea with an ID of editor. And say you're also loading the CKEditor jQuery Adapter. Inside a JavaScript file within client, put:
Template.edit.rendered = function() {
$('#editor').ckeditor();
}
The key here is that the initialization happens after the textarea editor exists and is ready, because this code is executed after the edit template is fully rendered. It will be reexecuted anytime edit is rerendered. Any other client-side library is included and initialized similarly.
EDIT Image files referenced via .css are a pain in Meteor. The "proper" way to deal with them is to put them all under the folder public, in this case for example public/ckeditor. Then edit the CKEditor .css files so that all references to image URLs point to your new folder at the root, i.e. /ckeditor/image1.png etc. (leave out "public").

Can I use CSS assets and the public folder in the same app?

I have a legacy application that I'd like to migrate to use the Rails 3 asset pipeline. I have upwards of 100 stylesheets which are imported on a template by template basis using a
content_for :stylesheets
block.
Compiling them all into a single stylesheet is not currently a goer as the code was written to expect only certain stylesheets on certain pages, so for example the login page imports a stylesheet which redefines article form. It would not be good to redefine this on all pages.
Is there a way to migrate slowly to the assets pipeline having the app look first for a compiled asset, and then having it failover to the public/stylesheets directory?
If you want to do this "right", one thing that would not take a huge amount of effort would be to put a class name on your <html> or <body> tag and wrap the contents of your CSS files with a selector. For example, I use something similar to the following in my ApplicationController.
before_filter :body_class
def body_class
controller_name = self.class.name.gsub(/Controller$/, '')
if !controller_name.index('::').nil?
namespace, controller_name = controller_name.split('::')
end
#body_classes = ["#{controller_name.underscore}_#{action_name} ".downcase.strip]
#body_classes = ["#{namespace.underscore}_#{#default_body_classes.join}".strip] if !namespace.nil?
end
Then, in my layout I have something similar to this
<body class="<%= #default_body_classes.join(' ') %>">
Next, you could change the extension for all of your stylesheets to .css.scss. Put them all in the new app/assets/stylesheets directory. To include them quickly (though possibly out of order), add
/*
* =require_tree .
*/
to the top of a new app/assets/application.css.scss file. Just include this one application.css file in your layouts.
<%= stylesheet_link_tag "application", :media => 'screen' %>
Finally, spend some time going through your stylesheets wrapping the entirety of each document with the appropriate body class. For example, if you have a stylesheet specific to some User::Admin controller's signup action, you would wrap the entirety of that stylesheet with
.user_admin.signup {
/* Your stylesheet's content here */
}
Sass will prefix all nested content properly with the .user_admin.signup class that will be appended to your <body> tag when that action's being rendered.
I realize this isn't really an intermediate fix as you're looking for, but following steps similar to this you should be able to get 95% of the way there with not much effort, and it will be done "right".

Resources