Plone: custom variable in content rule - plone

I have defined a dexterity content type myaddon.subscriber (subscriber = participant at an event).
I added a Content rule to send an email when a subscriber is approved (workflow state change to this one).
Each subscriber has an email address (email - field).
In Edit Mail Action form I see I can use variables like ${user_email} in Email recipients (Required)
The email where you want to send this message. To send it to different email addresses, just separate them with ,
This is working. In my case user_email is the email of logged in user - the person who approve the participant. The messages are sent when the state is changed. Perfect.
I need to define a variable ${subscriber_email} that will have the myaddon.subscriber.email value. How can I do this? I'm trying to find an example. So, how to use an field (email) of current changed object (subscriber) as variable in this Mail Action?

You can use IContextWrapper from plone.stringinterp 1.0.14+:
>>> new_context = IContextWrapper(context)(
... email=u"subscriber#example.com"
... )
>>> class SubscriberEmailSubstitution(BaseSubstitution):
... def safe_call(self):
... return self.wrapper.email
>>> sm = getGlobalSiteManager()
>>> sm.registerAdapter(SubscriberEmailSubstitution, (Interface, ), IStringSubstitution, name=u"subscriber_email")
>>> getAdapter(new_context, IStringSubstitution, 'subscriber_email')()
u'subscriber#example.com'
Imports:
>>> from zope.interface import Interface
>>> from plone.stringinterp.interfaces import IStringSubstitution
>>> from plone.stringinterp.interfaces import IStringSubstitutionInfo
>>> from zope.component import getGlobalSiteManager, getAdapter
>>> from plone.stringinterp.adapters import BaseSubstitution
>>> from plone.stringinterp.interfaces import IContextWrapper
See more stringinterp doctests.
Also see a fully working example within eea.pdf add-on.

Related

Telegram Bot Python Inlinekeyboardmarkup does not work for all users in a group

I want to respond when someone sends the /start comand and display a text with a like/dislike button in a group.
Here is my sample code:
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import (
Updater,
CommandHandler,
CallbackQueryHandler,
ConversationHandler)
import logging
FIRST, SECOND = range(2)
keyboard = [[InlineKeyboardButton('👍', callback_data='0'),
InlineKeyboardButton('👎', callback_data='2')]]
def start(update, context):
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text(
'Test Message\n',
reply_markup=reply_markup
)
return FIRST
def main():
updater = Updater(
'TOKEN', use_context=True)
dp = updater.dispatcher
conv_handler = ConversationHandler(
entry_points=[CommandHandler('start', start)],
states={
FIRST: [CallbackQueryHandler(a, pattern='^'+str(0)+'$'),
CallbackQueryHandler(b, pattern='^'+str(2)+'$')]
},
fallbacks=[CommandHandler('start', start)]
)
dp.add_handler(conv_handler)
updater.start_polling()
updater.idle()
if __name__ == "__main__":
main()
The callbacks only work for the user who sent the /start command. Other users cannot click the button or there is no callback. if another user sends a /start command, both like/dislike buttons of both posts of the bot work for the two users.
Where is the mistake?
I want every user to be able to press the buttons and it triggers a callback. regardless of whether the user has ever sent the /start command
I am thankful for every help.
The issue here is that by default conversations are per user - please also see this faq entry for details.
For your use case, I doubt that a ConversationHandler gives you any benefit. I would just register the CommandHandler and the CallbackQueryHandler independently.
Disclaimer: I'm currently the maintainer of python-telegram-bot.

How do I add a new connection type to Airflow?

Looking at the plugin documentation (https://airflow.incubator.apache.org/plugins.html), it is clear how to add to new hooks and operators, but I am adding a new hook that requires connection information. This information seems to be hard-coded in airflow/models.py. Is there a way to add my own connection type to the list without altering Airflow's source code?
airflow Connection's conn_type field allows null value. so if you don't care about giving unique type name to your custom hook, then you can give a default connection value in your hook implementation.
from airflow.exceptions import AirflowException
from airflow.hooks.base_hook import BaseHook
from airflow.utils.db import provide_session
class MyHook(BaseHook):
# ... impl whatever you want.
#classmethod
#provide_session
def get_hook(cls, conn_id='myhook_default', session=None):
try:
conn = cls.get_connection(conn_id)
except AirflowException:
# create default connection. run only once.
conn = Connection(
conn_id=conn_id,
# conn_type='string500', # You can give new type string here. But no UI component's for you. Just leave it.
host='default.example.com',
port=80,
login='default_login',
password='default_pw',
extra=json.dumps({...extra_defult_you_need...}),
)
session.add(conn)
session.commit()
return MyHook(conn=conn)

Plone: Notify a user on deleting his account

Using a subscriber on IPrincipalDeletedEvent is not a solution because the user is already deleted and I can't get his email address.
<subscriber
for="* Products.PluggableAuthService.interfaces.events.IPrincipalDeletedEvent"
handler="mycontent.userDeleted" />
https://github.com/plone/Products.PlonePAS/blob/4.2/Products/PlonePAS/pas.py#L78
api.user.get(userid=user_id) is None when my userDeleted(user_id, event) is called.
It seems adding a content rule for user removed is working the same.
Any idea how to get user's email address when his account is marked to be deleted? I just want to send him an email: Your account was deleted as you requested.
Monkey patching to add a event just before the user is deleted:
In patches.zcml:
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:monkey="http://namespaces.plone.org/monkey"
xmlns:zcml="http://namespaces.zope.org/zcml"
i18n_domain="myapp">
<include package="collective.monkeypatcher" />
<include package="collective.monkeypatcher" file="meta.zcml" />
<monkey:patch description="Add PrincipalBeforeDeleted event"
class="Products.PlonePAS.pas"
original="_doDelUser"
replacement="mycontent.patches._doDelUser"
docstringWarning="true" />
</configure>
In patches.py:
from zope.event import notify
from Products.PluggableAuthService.events import PrincipalDeleted
from Products.PlonePAS.interfaces.plugins import IUserManagement
from Products.PluggableAuthService.PluggableAuthService import \
_SWALLOWABLE_PLUGIN_EXCEPTIONS
from Products.PluggableAuthService.PluggableAuthService import \
PluggableAuthService
from Products.PlonePAS.pas import _doDelUser
from Products.PluggableAuthService.interfaces.events import IPASEvent
from zope.interface import implements
from Products.PluggableAuthService.events import PASEvent
class IPrincipalBeforeDeletedEvent(IPASEvent):
"""A user is marked to be removed but still into database.
"""
class PrincipalBeforeDeleted(PASEvent):
implements(IPrincipalBeforeDeletedEvent)
def _doDelUser(self, id):
"""
Given a user id, hand off to a deleter plugin if available.
Fix: Add PrincipalBeforeDeleted notification
"""
plugins = self._getOb('plugins')
userdeleters = plugins.listPlugins(IUserManagement)
if not userdeleters:
raise NotImplementedError(
"There is no plugin that can delete users.")
for userdeleter_id, userdeleter in userdeleters:
# vvv Custom
notify(PrincipalBeforeDeleted(id))
# ^^^ Custom
try:
userdeleter.doDeleteUser(id)
except _SWALLOWABLE_PLUGIN_EXCEPTIONS:
pass
else:
notify(PrincipalDeleted(id))
PluggableAuthService._doDelUser = _doDelUser
Then added a subscriber for this event:
In configure.zcml:
<subscriber
for="* mycontent.patches.IPrincipalBeforeDeletedEvent"
handler="mycontent.globalhandlers.userBeforeDeleted" />
In globalhandlers.py:
from Products.CMFCore.utils import getToolByName
def handleEventFail(func):
def wrapper(*args, **kwargs):
try:
func(*args, **kwargs)
except Exception:
logger.exception('in {0}'.format(func.__name__))
return wrapper
#handleEventFail
def userBeforeDeleted(user_id, event):
""" Notify deleted user about this action. """
membership_tool = getToolByName(api.portal.get(), 'portal_membership')
user = membership_tool.getMemberById(user_id)
email = user.getProperty('email')
mail_text = """
Hi!
Your account ({0}) was deleted.
Best regards,
Our Best Team""".format(user_id)
print mail_text
print email
# TODO send mail

How to get a Telegram Channel Id without sending a message to it

Is there any way to find a telegram channel id without sending a message to it ?
Right now I go this way and find the channel id by calling this URL in my code and get the JSON as result:
https://api.telegram.org/bot???????/sendMessage?chat_id=#?????&text=123
However, this cause sending the message "123" to the channel which is not good.
Check this link for help.
It is using web telegram url param info.
Adding details from the referred link:
log in web telegram
Click on the target channel then you will find the url displayed on your browser.
If it's a public channel, the ID is #name of the channel.
If it's a private channel then the url must be similar to:
https://web.telegram.org/#/im?p=c1018013852_555990343349619165
For this case, the channel ID would be 1018013852.
It's important to know that channel's IDs are always negative and 13 characters long!
So add -100 to it, making the correct ID -1001018013852.
You Can use Web Telegram to see each channel id in link of that page in your explorer
or
Just Simply Forward a message from your channel to This Bot: (https://telegram.me/getidsbot)
Yes, just forward a message of the channel to your bot and get the message.forward_from_chat.id.
Get your channel link
install python library telethon
get your api_id and api_hash here.
write the following code and run:
from telethon import TelegramClient
api_id=
api_hash=
channel_link = 'your_channel_link'
client = TelegramClient(session_name, api_id, api_hash,
update_workers=4, spawn_read_thread=False)
client.start()
entity = client.get_input_entity(**channel_link**)
print(entity.channel_id)
By the way, if you use it for telegram bot, just add -100 before the printed id, this should be work!
You Don't Need to get Channels ID .
simple send Message with Channel User Like : #channel
Fixing #scruel's answer:
In [1]: api_id = ...
In [2]: api_hash = '...'
In [3]: channelLink = 'https://t.me/BTCST_Community_EN'
In [4]: from telethon import TelegramClient, events
In [5]: client = TelegramClient('test', api_id, api_hash)
In [6]: client.start()
Out[6]: <telethon.client.telegramclient.TelegramClient at 0x7fc90c352290>
In [7]: entity = await client.get_entity(channelLink)
In [8]: channelId = '-100' + str(entity.id)
In [9]: channelId
Out[9]: '-1001236496320'
Here's a CLI util to do it:
#!env/bin/python
import sys
import asyncio
from telethon import TelegramClient
import config
async def channel_id_from_link(client, channel_link):
return "-100" + str((await client.get_entity(channel_link)).id)
async def main(channel_link):
async with TelegramClient(
"test", config.api_id, config.api_hash
) as client:
channel_id = await channel_id_from_link(client, channel_link)
return channel_id
if __name__ == "__main__":
channel_link = sys.argv[1]
channel_id = asyncio.run(main(channel_link))
print(channel_id)
Test:
> ./TelegramChannelId.py https://t.me/binance_api_english
-1001134190352
Put your bot into your channel and go to https://api.telegram.org/bot/getUpdates.
If someone send message to that channel, you will get the channel ID there.

Programmatically created user is active?

Django 1.9.6
I just want to test if anonymous user is redirected to login page.
I've created some user. It is definitely not registered.
Could you help me understand, why its property is_active is set to True.
Well, I stopped at pdb breakpoint and:
-> pdb.set_trace()
(Pdb) request.user.is_active
True
I have to explicitly set user.is_active = False. But why is the user active by default is a mystery to me. Nobody has activated it, I would say.
The code is below.
from django.test import TestCase
from .views import HomePageView
from django.http.request import HttpRequest
from django.contrib.auth.models import User
class GeneralTest(TestCase):
def test_anonymous_user_redirected_to_login_page(self):
user = User(username='anonymous', email='vvv#mail.ru', password='ttrrttrr')
request = HttpRequest()
request.user = user
hpv = HomePageView()
response = hpv.get(request)
pdb.set_trace()

Resources