wagtail 4.0/ How return urls (templates) for preview in admin board from #path - wagtail

class ProductIndexPage(RoutablePageMixin, Page):
subpage_types = ['ProductPage']
parent_page_types = ['home.HomePage']
def live_categories(self):
all_products_live_id = ProductPage.objects.live().values_list('categories_id', flat=True)
list_live_id_uniqe = list(set(all_products_live_id))
live_cat = ProductCategory.objects.filter(id__in=list_live_id_uniqe).order_by('-id')
return live_cat
#path('')
#path('all-categories/')
def all_category_page(self, request):
productpages = self.get_children().live().order_by('-first_published_at')
return self.render(request, context_overrides={
'title': self.title,
'productpages': productpages,
'live_categories': self.live_categories,
})
#path('<str:cat_name>/', name='cat_url')
def current_category_page(self, request, cat_name=None):
productpages = ProductPage.objects.live().filter(categories__slug__iexact=cat_name).order_by \
('-first_published_at')
current_cat = self.live_categories().get(slug=cat_name).name
return self.render(request, context_overrides={
'title': "%s" % current_cat,
'productpages': productpages,
'live_categories': self.live_categories,
})
#path('<str:cat_name>/<str:prod_name>/')
def product_page(self, request, cat_name=None, prod_name=None):
product = ProductPage.objects.get(categories__slug__iexact=cat_name, slug=prod_name)
return self.render(request, context_overrides={
'product': product},
template="products/product_page.html",)
I can't edit page in wagtail admin menu from path:
#path('<str:cat_name>/<str:prod_name>/')
My page tree in admin:
root/products/<prod_name>
How to change route in Admin for editing pages from wagtal button or for preview in Admin?
I am newbie, plz show me example.
sorry for English)

With RoutablePageMixin there is only one page - and it displays differently depending on the parameter sent on the url. Unfortunately, this means you can't preview the category version of your page, current_category_page.
But it looks like the product page you are routing too as #path('str:cat_name/str:prod_name/') should be displaying the same page as you serve from the ProductPage's own url. So the ProductPage you see in the preview ProductIndexPage + 'str:cat_name/str:prod_name/'

Related

Django DRF + Allauth: OAuth2Error: Error retrieving access token on production build

We are integrating DRF (dj_rest_auth) and allauth with the frontend application based on React. Recently, the social login was added to handle login through LinkedIn, Facebook, Google and GitHub. Everything was working good on localhost with each of the providers. After the staging deployment, I updated the secrets and social applications for a new domain. Generating the URL for social login works fine, the user gets redirected to the provider login page and allowed access to login to our application, but after being redirected back to the frontend page responsible for logging in - it results in an error: (example for LinkedIn, happens for all of the providers)
allauth.socialaccount.providers.oauth2.client.OAuth2Error:
Error retrieving access token:
b'{"error":"invalid_redirect_uri","error_description":"Unable to retrieve access token: appid/redirect uri/code verifier does not match authorization code. Or authorization code expired. Or external member binding exists"}'
Our flow is:
go to frontend page -> click on provider's icon ->
redirect to {BACKEND_URL}/rest-auth/linkedin/url/ to make it a POST request (user submits the form) ->
login on provider's page ->
go back to our frontend page {frontend}/social-auth?source=linkedin&code={the code we are sending to rest-auth/$provider$ endpoint}&state={state}->
confirm the code & show the profile completion page
The adapter definition (same for every provider):
class LinkedInLogin(SocialLoginView):
adapter_class = LinkedInOAuth2Adapter
client_class = OAuth2Client
#property
def callback_url(self):
return self.request.build_absolute_uri(reverse('linkedin_oauth2_callback'))
Callback definition:
def linkedin_callback(request):
params = urllib.parse.urlencode(request.GET)
return redirect(f'{settings.HTTP_PROTOCOL}://{settings.FRONTEND_HOST}/social-auth?source=linkedin&{params}')
URLs:
path('rest-auth/linkedin/', LinkedInLogin.as_view(), name='linkedin_oauth2_callback'),
path('rest-auth/linkedin/callback/', linkedin_callback, name='linkedin_oauth2_callback'),
path('rest-auth/linkedin/url/', linkedin_views.oauth2_login),
Frontend call to send the access_token/code:
const handleSocialLogin = () => {
postSocialAuth({
code: decodeURIComponent(codeOrAccessToken),
provider: provider
}).then(response => {
if (!response.error) return history.push(`/complete-profile?source=${provider}`);
NotificationManager.error(
`There was an error while trying to log you in via ${provider}`,
"Error",
3000
);
return history.push("/login");
}).catch(_error => {
NotificationManager.error(
`There was an error while trying to log you in via ${provider}`,
"Error",
3000
);
return history.push("/login");
});
}
Mutation:
const postSocialUserAuth = builder => builder.mutation({
query: (data) => {
const payload = {
code: data?.code,
};
return {
url: `${API_BASE_URL}/rest-auth/${data?.provider}/`,
method: 'POST',
body: payload,
}
}
Callback URLs and client credentials are set for the staging environment both in our admin panel (Django) and provider's panel (i.e. developers.linkedin.com)
Again - everything from this setup is working ok in the local environment.
IMPORTANT
We are using two different domains for the backend and frontend - frontend has a different domain than a backend
The solution was to completely change the callback URL generation
For anyone looking for a solution in the future:
class LinkedInLogin(SocialLoginView):
adapter_class = CustomAdapterLinkedin
client_class = OAuth2Client
#property
def callback_url(self):
callback_url = reverse('linkedin_oauth2_callback')
site = Site.objects.get_current()
return f"{settings.HTTP_PROTOCOL}://{site}{callback_url}"
Custom adapter:
class CustomAdapterLinkedin(LinkedInOAuth2Adapter):
def get_callback_url(self, request, app):
callback_url = reverse(provider_id + "_callback")
site = Site.objects.get_current()
return f"{settings.HTTP_PROTOCOL}://{site}{callback_url}"
It is important to change your routes therefore for URL generation:
path('rest-auth/linkedin/url/', OAuth2LoginView.adapter_view(CustomAdapterLinkedin))
I am leaving this open since I think this is not expected behaviour.

How to create an invisible dummy page in Wagtail?

How can I create an invisible dummy page in Wagtail?
I need a "virtual" page object within Wagtail to build a menu for non Wagtail based pages and also external resources. (See my entry post here)
class MenuDummyPage(Page):
menu_description = models.CharField(max_length=255, blank=True)
menu_icon = models.CharField(max_length=255, blank=True)
menu_link = models.CharField(max_length=255, blank=True)
settings_panels = [
FieldPanel('menu_description'),
FieldPanel('menu_icon'),
FieldPanel('menu_link'),
]
def get_sitemap_urls(self):
return []
def serve(self, request):
pass
If I create the above page object then it is not listed within the generated wagtail sitemap.
But if I navigate on my own to that page manually the object is called. How can I stop this?
Example:
If I create a MenuDummyPage with the title "This is a test" then the system will automatically generate a slug => "this-is-a-test".
If I call "/this-is-a-test"/ in my browser wagtail is answering because the slug exists. How can I remove this behavior for my "MenuDummyPage" objects?
If what you mean by a dummy page is a page under which other pages are kept in the page tree, then you could do the following:
from django.http import HttpResponseRedirect
class Node(Page):
subpage_types = [your subpage types]
parent_page_types = [your parent page types]
link = models.CharField(max_length=255, default='', blank='True')
content_panels = Page.content_panels + [
FieldPanel('link')
]
def serve(self, request):
if self.link is not None:
return HttpResponseRedirect(self.link)
# To handle the situation where someone inadvertantly lands on a parent page, do a redirect
first_descendant = self.get_descendants().first()
if first_descendant:
return HttpResponseRedirect(first_descendant.url)
else:
return HttpResponseRedirect(request.site.root_page.url)
The optional link field allows you to define a link if you wish for this position in the page tree structure. The above, again, assumes that you are using this Page-based item as a placeholder in the Page tree so that you can have other Pages under it. As long as you don't render the url of this page in your template, then users should never know how to get to the url for the Node, but if someone does get to the url for a Node-type page, then the first_descendant logic handles this situation by sending them to either the first descendant of the Node or to the home page if no descendants of Node exist.
In your template (note the use of specific_class):
{% for item in menu_items %}
<li>
<a href="{% if item.specific.link and item.specific.link != '' %}{{ item.specific.link }}{% elif item.specific_class == 'Node'%}#{% else %}{% pageurl item %}{% endif %}">{{ item.title }
</a>
</li>
{% endfor %}

Django ImageField Upload

I am trying to use Django ImageField to allow a user to upload a profile picture. However, after browsing for the image, when I attempt to update the profile, the picture upload changes from the file name to "No file selected" and I receive "This file is required" error.
If it helps, I was following along with this tutorial. I do understand that he uses two character fields, but I was attempting to change it to handle FileFields. From other questions such as this one on stack overflow, I know that the form requires a request.FILES as well.
Here is my Views.py.
#login_required
def user_profile(request):
if request.method == 'POST':
form = UserProfileForm(request.POST, request.FILES, instance=request.user.profile)
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts/loggedin')
else:
user = request.user
profile = user.profile
form = UserProfileForm(instance=profile)
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('profile.html', args)
Additionally, I have these two lines in my settings.
AUTH_PROFILE_MODULE = 'userprofile.UserProfile'
MEDIA_ROOT = 'E:\Pictures\django_stuff'
Let me know if there is anything else that is required.
As requested by Erny
Forms.py
from django import forms
from models import UserProfile
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('logo', 'description')
Models.py
class UserProfile(models.Model):
user = models.OneToOneField(User)
logo = models.ImageField(upload_to = 'photos')
description = models.TextField()
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])

Sample for Edit Url for a FB Page Tab App

I am implementing the edit url FB Page Tab App. But it needs the initialization which should happen when the app is added to a fanpage by the admin.
I am looking for the initial callback/notification to my app-url when the app is loaded on the fanpage. (I have looked at this already - http://developers.facebook.com/docs/authentication/signed_request/).
I am looking for a sample that shows the handling of the signed_request in this case, from the fan-page-load and what details are available/etc..
Thanks !
Here's a sample of handling the signed request:
function parse_signed_request($signed_request, $secret) {
list($encoded_sig, $payload) = explode('.', $signed_request, 2);
// decode the data
$sig = base64_url_decode($encoded_sig);
$data = json_decode(base64_url_decode($payload), true);
if (strtoupper($data['algorithm']) !== 'HMAC-SHA256') {
error_log('Unknown algorithm. Expected HMAC-SHA256');
return null;
}
// check sig
$expected_sig = hash_hmac('sha256', $payload, $secret, $raw = true);
if ($sig !== $expected_sig) {
error_log('Bad Signed JSON signature!');
return null;
}
return $data;
}
function base64_url_decode($input) {
return base64_decode(strtr($input, '-_', '+/'));
}
in $data there will be a "page" object that has a "admin" boolean. This will tell you if the current user of the page tab application is an admin of the page that the app is a tab of.

Using Page anchors on django

I would like to have AppEngine render an html page that auto scrolls to an html anchor point. I'm not sure how and where to put that type of instruction.
template_values = {
'foo' : 'foo',
'bar': 'bar',
'anchor' : '#MyPageAnchor' # ?? Something like this...
}
path = os.path.join(os.path.dirname(__file__), fileName)
self.response.out.write(template.render(path, template_values))
Is this possible? How do I accomplish this?
There's nothing App Engine specific here. You simply have to redirect the user to a URL that includes the anchor (eg, http://example.com/foo#anchor).

Resources