How to check if current visitor is shop's admin? - admin

I would like to create a product that will be available in Shopify's storefront but only accessible for the shop administrator. Is there a way to identify if the current user is an admin via liquid? Or is there any other solution for this. Thanks!

If you're signed in as an admin, when rendering the {{ content_for_header }} include, it will contain some JavaScript to push the page content down to make room for the Shopify admin bar.
We can utilize capture to store the {{ content_for_header }} code as a liquid variable and then use the contains operator to check if admin_bar_iframe exists in the variable.
{% capture CFH %}{{ content_for_header }}{% endcapture %}{{ CFH }}
{% if CFH contains 'admin_bar_iframe' %}
{% assign admin = true %}
{% endif %}
{% if admin %}
<!-- User is an admin -->
{% else %}
<!-- User is not an admin -->
{% endif %}
Note: I've noticed that the Shopify admin bar doesn't populate at all times (I think its a bug). If your Shopify admin bar is not populating on your instance this will not work.

Just figured out this method that works. (Basically the same thing as the old method, just another way around!)
Detecting logged in admin viewing site:
{% if content_for_header contains 'adminBarInjector' %}
<script>
console.log("You're a logged in admin viewing the site!");
</script>
{% endif %}
Detecting admin in design mode:
{% if content_for_header contains 'designMode' %}
<script>
console.log("You're an admin in design mode!");
</script>
{% endif %}

Another approach would be to use Customer Accounts. Liquid provides a {{ customer }} object, which is only present when a user (customer) is logged in.
You can add a specific tag to an admin user and use liquid to verify if a tag is present:
{% if customer.tags contains "admin" %}
And of course you need to identify your product as 'admin-only', for example using tags:
{% if customer.tags contains "admin" and product.tags contains "admin" %}
<!-- render product -->
{% else %}
<!-- do nothing -->
{% endif %}

EDIT: This seems not to be working anymore since an update to Shopify.
I know this is late, but here is what I've used and it has worked correctly in my testing. This is adapted from the previous answers, but allows use for Customise Theme options, etc.
{% capture CFH %}{{ content_for_header }}{% endcapture %}{{ CFH }}
{% assign isAdmin = true %}
{% if CFH contains '"__st"' %}
{% if CFH contains 'admin_bar_iframe' %}{% else %}
{% assign isAdmin = false %}
{% endif %}
{% endif %}

This is a more complete version of that provided by kyle.stearns above (I can't comment on it because new). One of the main instances where admin bar doesn't load is when previewing themes. Here is the amended code which I've used (updated June 2018 - as Shopify edited the way we preview themes):
{% capture CFH %}{{ content_for_header }}{% endcapture %}
{% if CFH contains 'admin_bar_iframe' %}
{% assign admin = true %}
{% elsif CFH contains 'preview_bar_injector-' %}
{% assign admin = true %}
{% endif %}
{% if admin %}
<!-- User is an admin -->
<script>
alert ("do some work");
</script>
{% else %}
<!-- User is not an admin -->
<script>
alert ("please buy some stuff");
</script>
{% endif %}
If you're using Plus then you also have access to the User via api.

I know this is an old question, but it still shows on top in google searches.
There's now a much better way.
Using liquid:
{% if request.design_mode %}
<!-- This will only render in the theme editor -->
{% endif %}
Using javascript:
if (Shopify.designMode) {
// This will only render in the theme editor
}
Source: shopify.dev

Currently there isn't. You could perhaps try inspecting cookies and stuff to see if there's some identifying information that would let you know if the user is an admin, but it would be fragile.
This would also require rendering the items, but hiding them via CSS. Then you'd show them using JS after you've run your checks.
As stated, this would probably be really fragile.

Related

How to add custom view to django admin interface?

My django admin interface looks like this:
Now I would like to add a view which does not correspond to a model.
I could overwrite the template of above page and add a custom link. But I think this would look ugly.
Example for overwriting admin/index.html:
{% extends "admin/index.html" %}
{% block content %}
{{ block.super }}
<div class="app-sonstiges module">
....
</div>
{% endblock %}
But maybe there is an official way to do add a custom view to the admin interface?
In my case I want to provide a form which can execute tcptraceroute to a remote server. The admin of my app needs this.
I used the same html tags. Now the link "tcptraceroute" looks nice, but unfortunately the messages moved down:
Is there a way to get a custom part like "Sontiges ... tcptraceroute" like in the screenshot, without moving the latest actions down?
Here is how the html structure looks like. My <div class="app-sonstiges"> is below content-main:
You have 3 options here:
Using third-party packages which provide menu
This is pretty straight forward, there are some good packages out there which support menu for admin, and some way to add your item to the menu.
An example of a 3rd party package would be django-admin-tools. The drawback is that it is a bit hard to learn. Another option would be to use django-adminplus but at the time of writing this, it does not support Django 2.2.
Hacking with django admin templates
You can always extend admin templates and override the parts you want, as mentioned in #Sardorbek answer, you can copy some parts from admin template and change them the way you want.
Using custom admin site
If your views are supposed to only action on admin site, then you can extend adminsite (and maybe change the default), beside from adding links to template, you should use this method to define your admin-only views as it's easier to check for permissions this way.
Considering you already extended adminsite, now you can use the previous methods to add your link to template, or even extend get_app_list method, example:
class MyAdminSite(admin.AdminSite):
def get_app_list(self, request):
app_list = super().get_app_list(request)
app_list += [
{
"name": "My Custom App",
"app_label": "my_test_app",
# "app_url": "/admin/test_view",
"models": [
{
"name": "tcptraceroute",
"object_name": "tcptraceroute",
"admin_url": "/admin/test_view",
"view_only": True,
}
],
}
]
return app_list
You can also check if current staff user can access to module before you show them the links.
It will look like this at then end:
Problem here that your div is not inside main-content. I suggest extending admin/index.html
Put in app/templates/admin/index.html
{% extends "admin/index.html" %}
{% load i18n static %}
{% block content %}
<div id="content-main">
{% if app_list %}
{% for app in app_list %}
<div class="app-{{ app.app_label }} module">
<table>
<caption>
{{ app.name }}
</caption>
{% for model in app.models %}
<tr class="model-{{ model.object_name|lower }}">
{% if model.admin_url %}
<th scope="row">{{ model.name }}</th>
{% else %}
<th scope="row">{{ model.name }}</th>
{% endif %}
{% if model.add_url %}
<td>{% trans 'Add' %}</td>
{% else %}
<td> </td>
{% endif %}
{% if model.admin_url %}
{% if model.view_only %}
<td>{% trans 'View' %}</td>
{% else %}
<td>{% trans 'Change' %}</td>
{% endif %}
{% else %}
<td> </td>
{% endif %}
</tr>
{% endfor %}
</table>
</div>
{% endfor %}
<!-- here you could put your div -->
<div class="app-sonstiges module">
....
</div>
<!-- here you could put your div -->
{% else %}
<p>{% trans "You don't have permission to view or edit anything." %}</p>
{% endif %}
</div>
{% endblock %}
The another approach is to add custom app to app_list. But it is far more ugly. So I suggest overriding template.
I assume you are overriding get_urls in AdminSite.
If you need a custom page to handle user input (from form), you may want to give django-etc 1.3.0+ a try:
from etc.admin import CustomModelPage
class MyPage(CustomModelPage):
title = 'My custom page' # set page title
# Define some fields you want to proccess data from.
my_field = models.CharField('some title', max_length=10)
def save(self):
# Here implement data handling.
super().save()
# Register the page within Django admin.
MyPage.register()

Factorizing a header menu in Django template

I'm building a website using django with a header on top of every page, which basically is a menu with a few links, constant throughout the pages.
However, depending on the page you're on I'd like to highlight the corresponding link on the menu by adding the class "active". To do so, I am currently doing as follow: each page has a full menu block that integrates within a general layout, which does NOT contain the menu. For exemple, page2 would look like this:
{% extends "layout.html" %}
{% block menu %}
<li>Home</li>
<li>page1</li>
<li class="active">page2</li>
<li>page3</li>
{% endblock %}
The problem is that, beside from that solution being not so pretty, every time I want to add a link to the header menu I have to modify each and every page I have. Since this is far from optimal, I was wondering if any of you would know about a better way of doing so.
Thanks in advance!
You can create a custom templatetag:
from django import template
from django.core.urlresolvers import reverse, NoReverseMatch, resolve
register = template.Library()
#register.simple_tag
def active(request, view_name):
url = resolve(request.path)
if url.view_name == view_name:
return 'active'
try:
uri = reverse(view_name)
except NoReverseMatch:
uri = view_name
if request.path.startswith(uri):
return 'active'
return ''
And use it in the template to recognize which page is loaded by URL
<li class="{% active request 'car_edit' %}">Edit</li>
If you have a "page" object at every view, you could compare a navigation item's slug to the object's slug
navigation.html
<ul>
{% for page in navigation %}
<li{% ifequal object.slug page.slug %} class="active"{% endifequal %}>
{{ page.title }}
</li>
{% endfor %}
</ul>
base.html
<html>
<head />
<body>
{% include "navigation.html" %}
{% block content %}
Welcome Earthling.
{% endblock %}
</body>
</html>
page.html
{% extends "base.html" %}
{% block content %}
{{ object }}
{% endblock %}
Where navigation is perhaps a context_processor variable holding all the pages, and object is the current PageDetailView object variable
Disclaimer
There are many solutions for your problem as noted by Paulo. Of course this solution assumes that every view holds a page object, a concept usually implemented by a CMS. If you have views that do not derive from the Page app you would have to inject page pretenders within the navigation (atleast holding a get_absolute_url and title attribute).
This might be a very nice learning experience, but you'll probably save loads time installing feinCMS or django-cms which both define an ApplicationContent principle also.
You may use the include tag and pass it a value which is the current page.
For example, this may be a separate file for declaring the menu template only:
menu.html
{% if active = "a" %}
<li>Home</li>
{% if active = "b" %}
<li>page1</li>
{% if active = "c" %}
<li class="active">page2</li>
{% if active = "d" %}
<li>page3</li>
And call this from within your template like this:
{% include 'path/to/menu.html' with active="b"%} # or a or c or d.
Hope it helps!

Django-CMS show_placeholder not working as expected

I'm working on a site where the footer content is shared across all pages. What is the best way to do in Django-CMS?
I tried using show_placeholder tag, but it somehow didn't work. A little more details on what I did:
First, I have a {% placeholder footer_info %} in base.html. Then I add a page called "Home" (template homepage.html) in django admin and put some text under footer_info as a Text plugin. As the accepted answer in this question suggested (http://stackoverflow.com/questions/3616745/how-to-render-django-cms-plugin-in-every-page),
I add
{% placeholder footer_info or %}
{% show_placeholder footer_info "Home" %}
{% endplaceholder %}
In a template called services.html which I used as the template for page Services.
However, the content in home page is not showing up in services page. I also tried adding an id home_cms_page to home page in the Advanced option area, so that I can reference it in services.html like this:
{% placeholder footer_info or %}
{% show_placeholder footer_info "home_cms_page" %}
{% endplaceholder %}
But the content is still not showing up.
Could anyone tell me what I am doing wrong? And is this the best way of getting some content from a page across all other pages (and I have to add show_placeholder in every other page)?
Thank you
EDIT:
It is not a multilingual site. I commented out 'cms.middleware.multilingual.MultilingualURLMiddleware', because the only language I use on the site is English.
I have this in my base.html:
{% load cms_tags sekizai_tags %}
<!-- all the rest of the HTML markups -->
<div class="span4">
{% placeholder footer_info %}
</div>
Then I added a page in the admin called "Home" with a Text plugin and an id of "home_cms_page".
The following is in my services.html:
{% extends "base.html" %}
{% load cms_tags %}
{% block base_content %}
{% placeholder services_info %}
{% endblock base_content %}
{% block page_content %}
Home page
{% endblock page_content %}
{% placeholder "footer_info" or %}
{% show_placeholder "footer_info" "home_cms_page" %}
{% endplaceholder %}
Read the documentation:
If you know the exact page you are referring to, it is a good idea to
use a reverse_id (a string used to uniquely name a page) rather than a
hard-coded numeric ID in your template. For example, you might have a
help page that you want to link to or display parts of on all pages.
To do this, you would first open the help page in the admin interface
and enter an ID (such as help) under the ‘Advanced’ tab of the form.
Then you could use that reverse_id with the appropriate templatetags:
{% show_placeholder "right-column" "help" %}
I added "index" in the advanced options of the index page, and added {% show_placeholder "banner" "index" %} in the base template. It all works.

Embedded controller views with assets

I am currently working on a symfony2 application and am using embedded controllers. My embedded controllers are like widgets which should encapsulate its own set of functionality and can be embedded anywhere and still be expected to function.
I have a controller called users online. The view it generates is simple, just a list of online users. But, I would like to add some javascript to that view so that I can use ajax to retrieve information for a user that's clicked on.
The controller basically returns a view:
return $this->render('AppBundle:Users:usersOnline.html.twig', array('somedata' => $data);
Here's the view for that controller:
{% extends partial.html.twig" %}
{% block content %}
<ul>
<li>User 1</li> (this would all be generated using 'somedata')
<li>User 2</li>
....
<ul>
{% endblock content %}
{% block scripts %}
..some javascript for interacting with this widget
{% endblock %}
This is the partial that is extended from:
{% block content %}
{% endblock content %}
{% block scripts %}{% endblock %}
Here's the main page that embeds the controller:
{% "base.html.twig" %}
{% block title %}Main{% endblock %}
{% block content %}
..some markup here
<div id="usersonline">
{% render "AppBundle:Users:usersOnline" with {'max': 4} %}
</div>
{% endblock %}
{% block scripts %}
..some javascript
{% endblock %}
This is the base that it extends:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{% block title %}{% endblock %} - App</title>
...Some stylesheets
</head>
<body>
{% block content %}{% endblock %}
<script src="http://yui.yahooapis.com/3.5.0pr2/build/yui/yui-min.js"></script>
{% block scripts %}{% endblock %}
</body>
</html>
The problem I am facing now is including javascripts from the embedded controller. In my case, the view extends partial and is fully rendered as 1 unit before it is inserted into the main page. In this case, I can only put my javascript in the content block, which means I will have <script> tags within <div> tags. I would also prefer to have scripts at the end of the body for user interface performance.
How should I structure my templates (or is it even possible) so that I can render the appropriate pieces from an embedded controller's view into appropriate blocks in the template that embeds the controller? In my current template the YUI library would be loaded after the embedded controller's rendered HTML markup, so accessing using the YUI library within the embedded controller would be impossible.
I have written an extension which is essentially a token parser to deal with this. The documentation for the token parser is very sparse and the code is also mostly uncommented, so it took a bit of fiddling around to get working.
I have structured the parser so that at the beginning, you would declare something (similiar to {% use %}):
{% myparser "AppBundle:Users:usersOnline" with {'max': 4} as xyz %}
Then blocks like xyzcontent would be avaliable and then you just render them in the appropriate places using {{ block('xyzcontent') }}. It works quite well. However the following does not work:
{% set max = 4 %}
{% myparser "AppBundle:Users:usersOnline" with {'max': max} as xyz %}
I could not find a way to evaluate an expression and get its value directly within the token parser. For the most part it works fine, except for this problem.
If someone has some idea as to how to get the value of a variable within the token parser, it would be great :)
After one day of looking for a satisfying solution, I finally choose for the following approach:
Render a controller for your widget's functionality (like you are already doing);
Render templates to embed your widget's assets (controllers are not needed and will waste your performance). Ignore include if the template doesn't exist.
Use a consistent naming convention to make the widget functionality more dynamic.
Example for one widget:
{% "base.html.twig" %}
{% block title %}Main{% endblock %}
{% block content %}
..some markup here
<div id="usersonline">
{{ render(controller("AppBundle:Users:usersOnline", {'max': 4})) }}
</div>
{% endblock %}
{% block scripts %}
{{ include("AppBundle:Users:usersOnline_js.html.twig" ignore missing }}
{% endblock %}
{% block stylesheets %}
{{ include("AppBundle:Users:usersOnline_css.html.twig" ignore missing }}
{% endblock %}
(As you can see, I use the Symfony 2.2 way of including and embedding)
Or, a more dynamic way of widget rendering:
{% "base.html.twig" %}
{% block title %}Main{% endblock %}
{% block widgets %}
..some markup here
{% for widget in widgets %}
{{ render(controller(widget.controller ~ ':widget', widget.options)) }}
{% endfor %}
{% endblock %}
{% block scripts %}
{% for widget in widgets %}
{{ include(widget.controller ~ ':widget_js.html.twig' ignore missing }}
{% endfor %}
{% endblock %}
{% block stylesheets %}
{% for widget in widgets %}
{{ include(widget.controller ~ ':widget_css.html.twig' ignore missing }}
{% endfor %}
{% endblock %}
My conclusion
Rendering multiple controllers in one page, while "merging" it's templates blocks, cannot be achieved out of the box in Symfony2. Symfony2 has always been focusing on one controller for each request, which explains why multiple controllers outputs are separated and hard to combine.
I think the Symfony CMF has (or will have) a proper solution for this, because this whole widget topic is more applied in dynamic content managed websites.
Anyway, I think my way of combining embedded controllers and included templates is the best approach for Symfony 2 so far.
The alternatives, in my opinion, do have to many disadvantages:
Using multiple controllers: too heavy and not needed because assets won't need any controller.
Loading the widgets assets directly in the page: hard to maintain when changing widgets / assets.

Django change the admin page label 'Auth' to 'Authentication'

How can i change the display label Auth in the Django admin dashboard to Authentication?
There is currently no easy/elegant way to do this. Customisable app-labelling has been a sore point for some time. You can override admin/index.html and inject some javascript code to change the labelling. Note that you could also change admin.site.index_template to something like "admin/my_index.html", which can then use {% extends "admin/index.html" %} to keep things DRYer.
Of course there are other areas in the admin that "Auth" will appear also, such in "admin/app_index.html", the breadcrumbs, etc...
I imagine if your overwriting admin/index.html you could hard code the logic in the template instead of any javascript:
<caption><a href="{{ app.app_url }}" class="section">
{% ifequal app.name "Auth" %}
{% trans 'Authentication' %}
{% else %}
{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}
{% endifequal %}
</a></caption>