I have a sets of users that I need to create themes for. Depending on the user's account association I want to load different themes. For example if John Doe logs in I need to load up greenteam.scss. However, if Doug Smith logs in he should see blueteam.scss.
Those files are basically color and font settings used by other elements. greenteam may have $header-bg: green; and blueteam would have $header-bg: blue;.
My initial thought is to have a structure like this: /src/scss/client/_client-xxx.scss that says the background colors, font colors, etc variables set in the application. So, after login the system is told the person logged in is part of "greenteam" and /src/scss/client/_client-greenteam.scss is loaded to compile the colors and other settings together. This way I can just drop the new client theme in the directory and boom, new theme!
The number of "teams" is going to grow exponentially. I have no idea how many I'll ever have at any given point. This is why the "just change the body class" is worrisome as that will get very big and hard to manage.
It's also possible I don't have a good grasp on how angular builds itself out on the web as a user calls the pages/styles. It appears, at a glance, when deployed on the server angular compiles the scss down to css in the header and not loaded "on the fly". If that's the case it seems I'd have to load all the possible client css options and just show what matches (the body class route).
The user won't have control over their theme directly or be able to change it, the theme will be customized manually by a developer.
I would have suggested to change the body class, but, if you really want to apply css at runtime, you could try to:
compile the scss to css
insert it in the dom at runtime depending on the user
Check this topic which tells how to add a style at runtime, you could get the text content of the stylesheet from a database:
https://stackoverflow.com/a/524717/10899694
Related
icon_expr is gone.
There is an related issue https://github.com/plone/Products.CMFPlone/issues/1236
concerning the problem but only for control-panel icons.
What is the appropriate way to add a icon to a dexterity content type in plone 5?
Any pointer welcome
Volker
Unluckily the Plone 5 way is only through CSS (with a background image for example) and register it in the new resource registry.
One way I tested is to re-use Fontello, like Barceloneta is doing but is not really simple because you must manually modify the generated CSS to prevent you new CSS to destroy other global rules.
An example is here: https://github.com/RedTurtle/TurtledGazette/tree/master/Products/PloneGazette/browser/static (it's not even Dexterity or Archetypes, but this is not important).
Note. I don't really understand this run to CSS and iconfont. It's a kind of over-optimization:
old school images can still be cached
background images are not really accessible as real images provided also an "alt" for blind people, that warn about the content type.
I don't like current situation too much... it's OK while you are developing a theme but is a nightmare for add-ons developers.
Beneath the update of an actual fontello font, you can limit yourself to enhance the icon configuration.
From the Products.CMFPlone package you can get the current icon font config file "config.json"
/Products/CMFPlone/static/fonts/config.json
To review the content of the file visually, go to the fontello.com website and visit the menu under the "wrench" icon.
Reset all icon selections and settings you may have done before.
Unselect glyphs
Reset all changes
Use Import to load the config.json file from CMFPlone.
Review the icons, names, codes and shapes
Look at the source code of config.json and locate icons not referencing a font but containing plain svg path information.
You will figure out that even glyphs can contain multiple path elements and holes as well.
Try to overload just the config.json file in your package by appending your custom icon as plain svg in the json and choose a non conflicting unicode char code. I am still working on documenting this in detail.
Source: I found a hint to this by Victor Fernandez de Alba mentioning this method in his talk [2] (see transscript [1]) during Plone Conf Bucharest:
http://maurits.vanrees.org/weblog/archive/2015/10/victor-fernandez-de-alba-plone-5-theming
http://www.slideshare.net/sneridagh/plone-5-theming-53980481 Slide 12
I am new to Plone and I'm just learning how to change stylesheets.
I successfully changed the ploneCustom.css in /Plone/portal_skins/custom/ and saved it.
But when I tried to take a look at the site, I realised that the ploneCustom.css stylesheet isn't even loaded.
According to my research a template is broken...
But what do I do with that information?
Where do I fix it and how?
Thanks for your help in advance!
50 ways of styling Plone, four of them explained:
1.) Customize ploneCustom.css
A relict of the skin-folder-times, by now it is recommended to use browser-based resources, instead.
The reason is, when you have a lot of resources registered, it's hard to keep a correct order of the skin-layers and that can lead to unwanted overrides. Nevertheless, if you don't have a complex setup, or for quick testing, it is feasible to use skin-layers and ploneCustom.css, the exact steps are:
Go to "http://yourhost.net:8080/yourPloneSiteId/portal_skins/sunburst_styles/ploneCustom.css/manage_main".
Click on "customize".
Enter some style for testing, e.g. "body { background: red }", save.
Make sure, CSS-debug-mode is on at "http://yourhost.net:8080/yourPloneSiteId/portal_css/manage_cssForm" also make sure that ploneCustom.css is actually on the list and enabled, meaning the checkbox on the left is checked. It should be the last entry in order to override all other stylesheets.
2.) Use a site's page as style-source
Alternatively you can have a look at "adi.ttw_styles" (disclaimer: I'm the author), which provides you with a stylesheet living in the site as an ordinary item of type "Page" and is registered as a browser-based resource, so naming-conflicts are impossible. Still, the stylesheet lives in the site then, which wouldn't be suitable for cases, where you want to have a reproducable theme.
3.) Include browser-based resources in your addon (f.k.a. "Product")
Well documented.
An addon-generator, which allows you to create an addon holding a browser-based stylesheet and javascript, is "adi.devgen" (disclaiming again: I'm the author), after installation simply execute devgen addBrowserSkel your.addonname of the commandline.
4.) Diazo
The widely promoted state-of-the-art-way-of-theming. Well documented, have fun learning XLST :-)
I have several large CSS files and making a change can sometimes take a few minutes just to find the right selector to change. I would like it if there was a nice ModX editor for CSS, but I haven't been able to find one. I am willing to settle for splitting up my files into parts, as long as my site still renders. Can I do that and how? If there is a nice editor (plugin?) instead, where can I find one?
I guess the real question is what kind of parts are acceptable for you. If you follow this question, you can begin the process of allowing ModX to manage your CSS. Once this happens, your options open considerably. Your CSS editing will then become easier and less time consuming depending on your level of expertise with ModX. This answer will be pretty simple, as it will show simply how to add a given selector as a resource. Other further development can be intuited from here, though.
CSS as a Resource
Once your CSS is being managed as a Resource (which takes about 15 minutes), you may utilize Templates, Template Variables, Chunks, Snippets and Plugins. Thisis actually pretty amazing, but setup can be a bit of a pain. You will basically be investing some time to save a lot of time in the future. The next logical step is split your Selectors accordingly, but you don't want to break what currently works. Having a fluid understanding of the getResources addon will be crucial to further development.
How to do it:
1. Create a new chunk
Click the Elements tab, and click "New Chunk". Name it "css-selector". Set the content to:
[[+pagetitle]] {[[+content]]}
It's as simple as that. Don't forget to click "Save"! This will let you set a Selector as a resource. It will use the title for the selector and content for the rules. You can forget about using those braces any more. Your new chunk will handle those from now on.
2. Adjusting your Template
Now, we just have to convince the template that it nows how to read parts, as well as not forget the whole. Open your CSS Stylesheet template (the one that says [[*content]] for its content). Adjust the code so that it has the following:
[[!getResources?
&parent=`[[*id]]`
&depth=`1`
&tpl=`css-selector`
&includeContent=`1`
&sortby=`menuindex`
&sortdir=`ASC`
&limit=`99`
]]
[[*content]]
Again, click "Save". Let me explain the Template real quick. If you have child, they'll get rendered first depending on their menu index. Further, it will render the contents of the document that are not children afterward. This will allow you to only make new resources for your most important selectors, while keeping the stuff that will never change in the main resource.
3. Create a new Template
This is so that your selectors don't do anything funny and just render the content. Create a new Template named "CSS Selector". Set its content to:
[[*content]]
4. Create a new Resource
Create a new Resource. Set the title to the selector for the css statement you want to manage. Then set the content to the rules without the braces. For instance, if your css statement is: div#header .logo {border:0;}, you'll set the title to div#header .logo and the content to border:0;. Set the resource alias to whatever you want. I use numbers for each one. Set the template to your new "CSS Selector". Important Now, set the Parent Document to your Stylesheet. Click Save.
5. Testing the Stylesheet
First, Right-click your new resource and choose "View Resource". This will just make sure that the statement was rendered correctly. It should simply say your rule in CSS format.
Next, Right-Click the Stylesheet resource and choose "View Resource". You should see the Selector at the top and all of the other rules below it.
Final Considerations
Observations
You'll notice that your child resources do not have to be changed to "CSS" for Document Type. Only the parent stylesheet has to be. This allows for some neat stuff as your expertise with ModX grows.
You can change the order of rules by simply changing the menu index of them.
The number of rules that can be done this way is based on the &limit variable in the getResources statement in your template. &limit applies to each stylesheet, so in this example you have 99 statements per stylesheet that may be separate resources.
A Note on Server Load
This will place load on the server as the number of resources goes up. For development, keep the "do not cache flag" (!) on your getResources statement. Once you are done, remove the exclamation mark and let it all be cached. This will save a ton of load.
Further Development
I added an isEnabled template variable to mine so I can turn on and off each rule as I pleased.
You may possibly begin to manage your CSS on the front-end utilizing FormIt.
Custom Manager Pages may even be a better option for you.
Further abstraction might allow you to create Groupings of statements for even further organization.
I am creating a site which will have a "desktop" and a "mobile" theme. I've two theme packages for this site: mysite.theme and mysite.mobile_theme. The mobile_theme is a stripped down version of the desktop theme, with new views and a reduced set of viewlets. I want to switch between these two themes based on the URL the site is visited from (i.e., mobile.mysite.com vs. www.mysite.com).
As the mobile and desktop themes will share a lot of code, mysite.mobile_theme descends from mysite.theme in the following ways:
1) mobile_theme GS skins.xml has a skin path based on the old theme, so the desktop theme's CSS etc. is used:
<skin-path name="mysite.mobile_theme" based-on="mysite.theme">
2) IThemeSpecific marker subclasses the original one, so views which I'm not overriding for the mobile site fallback to the ones in mysite.theme:
from mysite.theme.browser.interfaces import IThemeSpecific as IBaseTheme
class IThemeSpecific(IBaseTheme):
"""Marker interface that defines a Zope 3 browser layer.
"""
3) I have registered various views in mysite.mobile_theme to override the certain ones in mysite.theme.
4) I've used generic setup to have different viewlet registrations for each theme.
At this stage, if I select mysite.mobile_theme in the "Default skin" option portal skins->properties, everything works correctly: my views are used and the viewlets settings from the mobile_theme's GS profile are picked up correctly. So it appears the theme is set up correctly overall.
As mentioned above, however, I would like to swap between these two themes based on URL.
First, I swapped the "Default skin" back to "mysite.theme". I then created an access_rule in the root on my Plone site, roughly following these instructions to select a skin based on URL. It's at plonesite/access_rule and is set up as the access_rule for the plone site:
url = context.REQUEST.get('ACTUAL_URL', '')
if 'mobile' in url:
context.changeSkin('mysite.mobile_theme', context.REQUEST)
else:
context.changeSkin('mysite.theme', context.REQUEST)
I've also tried using context.REQUEST.set('plone_skin', 'mysite.theme') rather than calling context.changeSkin(...).
Using this setup, the viewlets displayed change correctly based on the URL I've used--so it looks like the skin is being changed at some point--but the mysite.mobile_theme's view classes/templates are not being used in preference to mysite.theme's. In summary:
If I call from a URL containing "mobile" I get mysite.theme's views, but mysite.mobile_theme's viewlet registrations.
Otherwise, I get mysite.theme's views and mysite.theme's viewlet registrations.
It looks like I might have to hook into the traversal mechanism to change it so if "mobile" is in the URL, the mysite.mobile_theme's views registered against its IThemeSpecific are chosen rather than the mysite.theme ones, but I'm not sure this is correct nor how I'd go about this.
Can anyone give me some pointers?
UPDATE 3hrs after originally asking
To answer my own question (which I can't do for another 5 hours due to SO's rules):
"""
It would appear that you must patch much lower down in the stack to make this work. I looked at how it was done in plone.gomobile, and they monkeypatch the skin choosing code itself. See:
http://code.google.com/p/plonegomobile/source/browse/gomobile.mobile/trunk/gomobile/mobile/monkeypatch.py
"""
You could use collective.editskinswitcher. Its main use case is to use the Plone Default theme on say edit.example.com and My Custom Theme on www.example.com. You can probably tweak its property sheet to fit your use case though.
Since the 'mobile theme' use case is fairly common I would accept patches to make that easier; or I may work on that myself some time.
(BTW, note that there is a fix-browser-layers branch that may help when you miss some items that are registered for a specific browser layer; seems ready to merge except that I would like to add some tests first.)
I have done this in some prototypes of mobile themes. Please consider thoses two addons not ready for production:
https://github.com/toutpt/plonetheme.jquerymobile
https://github.com/toutpt/plonetheme.senchatouch
The related code is:
The patch on browserlayer to mark the request with my theme layer: https://github.com/toutpt/plonetheme.jquerymobile/blob/master/plonetheme/jquerymobile/layer.py
The patch on plonetool to add ##mobile on every content page: https://github.com/toutpt/plonetheme.jquerymobile/blob/master/plonetheme/jquerymobile/PloneTool.py
The patch on skintool to tell skin layer is this one if browser layer: https://github.com/toutpt/plonetheme.jquerymobile/blob/master/plonetheme/jquerymobile/SkinsTool.py
If you are using plone.app.theming, you also can switch your diazo theme: https://github.com/toutpt/plonetheme.jquerymobile/blob/master/plonetheme/jquerymobile/transform.py
Do I understand correctly that at the mobile URL your skins are correct, but your Zope3 Views are not? Which makes sense to me, since the view classes are based on Interfaces. In the same code above, where you use context.changeSkin, add:
from zope.interface import alsoProvides
alsoProvides(context, IMobileView)
and have your view zcml specify for ... IMobileView
[edit: on second thoughts, this really should be what happens when you change the skin - where the additional inteface will be your "IThemeSpecific" - so I'm not sure what's at play here, but it wouldn't hurt to try alsoProvides(context, IThemeSpecific)]
So I'm writing a Django based website that allows users select a color scheme through an administration interface.
I already have middleware/context processors that links the current request (based on domain) to the account.
My question is how to dynamically serve the CSS with the account's custom color scheme.
I see two options:
Add a CSS block to the base template that overrides the styles w/variables passed in through a context processors.
Use a custom URL (e.g. "/static/dynamic/css/< website_id >/styles.css") that gets routed to a view that grabs all the necessary values and creates the css file.
I'm content with either option, but was wondering if anyone else out there has dealt with similar problems and could give some insight as to "Best Practices".
Update : I'm leaning towards option number 2, as I think this will allow for better caching down the road. So it's dynamic the first time, gets stored in memcache (or whatever), and invalidated when a user updates their settings in the admin site.
Update: Firstly, I'd like to thank everyone for their suggestions thus far. All the answers thus far have focused around generating static files. Though this would work great in production, it feels like a tremendous burden during development. If I wanted to add a new element to be styled, or tweak existing styles I'd have to go through and recreate each and every css file. Sure, this could be done with a management command, but I just don't feel it's worth it. Doing it dynamically would add 1 maybe 2 queries to each page load, which is something I'm not worried about at this stage. All I need to know is that at some point I will be able to cache it without rewriting the whole thing.
I've used option #2 with success. There are 2 decent ways of updating the generated static files that I know of:
Use a version querystring like /special_path.css?v=11452354234 where the v parameter is generated from a database field, key in memcached, or some other persistent file. Version gets updated by admin, or for development you would just make the generation not save if the parameter was something special like v=-1. You'll need a process to clean up the old generations after some time.
Don't use a version querystring, but have it look first for the generated file, if it can't find it, it generates it. You can create a cron job or WSGI app that looks for filesystem changes for development, and have a hook from your admin panel that deletes generations after an update. Here's an example of the monitoring, which you would have to convert to be specific to your generations and not to Django. http://code.google.com/p/modwsgi/wiki/ReloadingSourceCode#Monitoring%5FFor%5FCode%5FChanges
Could generate the css and store it in a textfield in the same model as the user profile/settings. Could then have a view to recreate them if you change a style. Then do your option 1 above.
Nice question.
I would suggest to pre-generate css file after colors scheme is saved. This would have positive impact on caching and overall page loading time. You can store your css files in directory /media/css/custom/<id or stometing>/styles.css or /media/css/custom/<id or sth>.css and in template add <link rel="stylesheet" href="/media/css/custom/{{some_var_pointing _to_file_name}}" />
You can also do the trick with some random number or date in css file name that could be changed each time file is saved. Thanks to this browser will load the file immediately in case of changes.
UPDATE: example of using model to improve this example
To make managing of those file easy you can create simple model (one per user):
class UserCSS(models.Model):
bg_color = models.CharField(..)
...
...
Fields (like bg_color) can represent parts of your css file. You can ovveride save method to add logic that creates css file for user (by rendering some template).
In case your file format change you can make changes in your's model definition (with some default values for new fields), make little changes in template and run save method for each exisintg instance of class. This would renew your css files.
That should work nicely.
I would create an md5 key with the theme elements, store this key in the user profile and create a ccs file named after this md5 key : you gain static file access and automatic theme change detection.