integrated object selector into edit family
svn: r5804
This commit is contained in:
@@ -8,7 +8,7 @@ class ObjectFrameFactory(object):
|
||||
__frame_creators = {ObjectTypes.PERSON: PersonFrame,
|
||||
ObjectTypes.FAMILY: FamilyFrame}
|
||||
|
||||
def get_frame(self,object_type,dbstate,uistate):
|
||||
return self.__class__.__frame_creators[object_type](dbstate,uistate)
|
||||
def get_frame(self,object_type,dbstate,uistate,filter_spec=None):
|
||||
return self.__class__.__frame_creators[object_type](dbstate,uistate,filter_spec)
|
||||
|
||||
|
||||
|
@@ -13,8 +13,8 @@ class FamilyFilterFrame(FilterFrameBase):
|
||||
|
||||
__default_border_width = 5
|
||||
|
||||
def __init__(self,dbstate,label="Filter"):
|
||||
FilterFrameBase.__init__(self,label)
|
||||
def __init__(self,dbstate,filter_spec=None,label="Filter"):
|
||||
FilterFrameBase.__init__(self,filter_spec,label)
|
||||
|
||||
# Gramps ID
|
||||
id_check = gtk.CheckButton()
|
||||
@@ -115,7 +115,13 @@ class FamilyFilterFrame(FilterFrameBase):
|
||||
self._table.attach(filter_label,self._label_col,self._label_col+1,current_row,current_row+1,xoptions=gtk.FILL,yoptions=False)
|
||||
self._table.attach(filter_combo,self._control_col,self._control_col+1,current_row,current_row+1,xoptions=gtk.EXPAND|gtk.FILL,yoptions=False)
|
||||
|
||||
if filter_spec is not None:
|
||||
self._set_filter(filter_spec)
|
||||
|
||||
|
||||
def _set_filter(filter_spec):
|
||||
pass
|
||||
|
||||
def on_apply(self,button):
|
||||
pass
|
||||
|
||||
|
@@ -29,12 +29,13 @@ class FamilyFrame(ObjectFrameBase):
|
||||
|
||||
def __init__(self,
|
||||
dbstate,
|
||||
uistate):
|
||||
uistate,
|
||||
filter_spec=None):
|
||||
|
||||
ObjectFrameBase.__init__(self,
|
||||
dbstate=dbstate,
|
||||
uistate=uistate,
|
||||
filter_frame = FamilyFilterFrame(dbstate),
|
||||
filter_frame = FamilyFilterFrame(filter_spec=filter_spec),
|
||||
preview_frame = FamilyPreviewFrame(dbstate),
|
||||
tree_frame = FamilyTreeFrame(dbstate))
|
||||
|
||||
|
@@ -1,9 +1,11 @@
|
||||
import os.path
|
||||
import gtk
|
||||
import gobject
|
||||
from logging import getLogger
|
||||
log = getLogger(".ObjectSelector")
|
||||
|
||||
import ImgManip
|
||||
import const
|
||||
|
||||
class FamilyPreviewFrame(gtk.Frame):
|
||||
|
||||
@@ -79,16 +81,16 @@ class FamilyPreviewFrame(gtk.Frame):
|
||||
image[image_no].set_from_pixbuf(pixbuf)
|
||||
image_no += 1
|
||||
else:
|
||||
self._image_l.set_from_file("../person.svg")
|
||||
self._image_r.set_from_file("../person.svg")
|
||||
self._image_l.set_from_file(os.path.join(const.rootDir,"person.svg"))
|
||||
self._image_r.set_from_file(os.path.join(const.rootDir,"person.svg"))
|
||||
|
||||
except:
|
||||
log.warn("Failed to generate preview for family", exc_info=True)
|
||||
self.clear_object()
|
||||
|
||||
def clear_object(self):
|
||||
self._image_l.set_from_file("../person.svg")
|
||||
self._image_r.set_from_file("../person.svg")
|
||||
self._image_l.set_from_file(os.path.join(const.rootDir,"person.svg"))
|
||||
self._image_r.set_from_file(os.path.join(const.rootDir,"person.svg"))
|
||||
|
||||
|
||||
if gtk.pygtk_version < (2,8,0):
|
||||
|
@@ -16,8 +16,10 @@ class FilterFrameBase(gtk.Frame):
|
||||
|
||||
__default_border_width = 5
|
||||
|
||||
def __init__(self,dbstate,label="Filter"):
|
||||
def __init__(self,filter_spec=None,label="Filter"):
|
||||
gtk.Frame.__init__(self,label)
|
||||
|
||||
self._filter_spec = filter_spec
|
||||
|
||||
align = gtk.Alignment()
|
||||
|
||||
@@ -54,6 +56,7 @@ class FilterFrameBase(gtk.Frame):
|
||||
|
||||
self.add(align)
|
||||
|
||||
|
||||
def on_apply(self,button):
|
||||
"""Build a GenericFilter object from the settings in the filter controls and
|
||||
emit a 'apply-filter' signal with the GenericFilter object as the parameter."""
|
||||
|
14
src/ObjectSelector/_FilterSpec.py
Normal file
14
src/ObjectSelector/_FilterSpec.py
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
class FilterSpecBase(object):
|
||||
|
||||
def __init__(self):
|
||||
self._gramps_id = None
|
||||
|
||||
def set_gramps_id(self,gramps_id):
|
||||
self._gramps_id = gramps_id
|
||||
|
||||
def get_gramps_id(self):
|
||||
return self._gramps_id
|
||||
|
||||
def include_gramps_id(self):
|
||||
return self._gramps_id is not None
|
14
src/ObjectSelector/_FilterSpecBase.py
Normal file
14
src/ObjectSelector/_FilterSpecBase.py
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
class FilterSpecBase(object):
|
||||
|
||||
def __init__(self):
|
||||
self._gramps_id = None
|
||||
|
||||
def set_gramps_id(self,gramps_id):
|
||||
self._gramps_id = gramps_id
|
||||
|
||||
def get_gramps_id(self):
|
||||
return self._gramps_id
|
||||
|
||||
def include_gramps_id(self):
|
||||
return self._gramps_id is not None
|
@@ -4,13 +4,18 @@ sys.path.append("..")
|
||||
sys.path.append(".")
|
||||
sys.path.append("ObjectSelector")
|
||||
|
||||
import os.path
|
||||
import gtk
|
||||
import gobject
|
||||
from gettext import gettext as _
|
||||
|
||||
import _Factories
|
||||
from _Constants import ObjectTypes
|
||||
from _ObjectSelectorResult import ObjectSelectorResult
|
||||
|
||||
from DisplayState import ManagedWindow
|
||||
import const
|
||||
|
||||
class _ObjectTypeWidgets(object):
|
||||
|
||||
def __init__(self):
|
||||
@@ -37,7 +42,7 @@ OBJECT_LIST = [ObjectTypes.PERSON, ObjectTypes.FAMILY,
|
||||
ObjectTypes.MEDIA, ObjectTypes.PLACE,
|
||||
ObjectTypes.REPOSITORY]
|
||||
|
||||
class ObjectSelectorWindow(gtk.Window):
|
||||
class ObjectSelectorWindow(gtk.Window,ManagedWindow):
|
||||
|
||||
__gproperties__ = {}
|
||||
|
||||
@@ -54,13 +59,21 @@ class ObjectSelectorWindow(gtk.Window):
|
||||
def __init__(self,
|
||||
dbstate,
|
||||
uistate,
|
||||
track,
|
||||
title = _("Select Object"),
|
||||
filter_spec = None,
|
||||
default_object_type = ObjectTypes.PERSON,
|
||||
object_list = OBJECT_LIST):
|
||||
|
||||
|
||||
# Init the display manager
|
||||
ManagedWindow.__init__(self,uistate,track,self)
|
||||
|
||||
# Init the Window
|
||||
gtk.Window.__init__(self)
|
||||
|
||||
self._dbstate = dbstate
|
||||
self._uistate = dbstate
|
||||
self._track = track
|
||||
self._object_list = object_list
|
||||
self._current_object_type = None
|
||||
|
||||
@@ -70,7 +83,7 @@ class ObjectSelectorWindow(gtk.Window):
|
||||
for object_type in object_list:
|
||||
self._object_frames[object_type] = _ObjectTypeWidgets()
|
||||
|
||||
self.set_title("Add Person")
|
||||
self.set_title(title)
|
||||
|
||||
# Selected object label
|
||||
|
||||
@@ -122,8 +135,8 @@ class ObjectSelectorWindow(gtk.Window):
|
||||
self.__class__.__default_border_width)
|
||||
|
||||
|
||||
person_pixbuf = gtk.gdk.pixbuf_new_from_file("./person.svg")
|
||||
flist_pixbuf = gtk.gdk.pixbuf_new_from_file("./flist.svg")
|
||||
person_pixbuf = gtk.gdk.pixbuf_new_from_file(os.path.join(const.rootDir,"person.svg"))
|
||||
flist_pixbuf = gtk.gdk.pixbuf_new_from_file(os.path.join(const.rootDir,"flist.svg"))
|
||||
|
||||
self._tool_list = gtk.ListStore(gtk.gdk.Pixbuf, str,int)
|
||||
|
||||
@@ -178,7 +191,7 @@ class ObjectSelectorWindow(gtk.Window):
|
||||
for object_type in object_list:
|
||||
|
||||
self._object_frames[object_type].frame = \
|
||||
_Factories.ObjectFrameFactory().get_frame(object_type,dbstate,uistate)
|
||||
_Factories.ObjectFrameFactory().get_frame(object_type,dbstate,uistate,filter_spec)
|
||||
|
||||
# connect signals
|
||||
self._object_frames[object_type].frame.connect(
|
||||
@@ -246,6 +259,8 @@ class ObjectSelectorWindow(gtk.Window):
|
||||
self._set_object_type(default_object_type)
|
||||
self.set_default_size(700,300)
|
||||
|
||||
self.show()
|
||||
|
||||
|
||||
def _set_object_type(self,selected_object_type):
|
||||
# enable selected object type
|
||||
|
@@ -19,8 +19,8 @@ class PersonFilterFrame(FilterFrameBase):
|
||||
|
||||
__default_border_width = 5
|
||||
|
||||
def __init__(self,dbstate,label="Filter"):
|
||||
FilterFrameBase.__init__(self,label)
|
||||
def __init__(self,filter_spec=None,label="Filter"):
|
||||
FilterFrameBase.__init__(self,filter_spec,label)
|
||||
|
||||
# Gramps ID
|
||||
self._id_check = gtk.CheckButton()
|
||||
@@ -86,13 +86,13 @@ class PersonFilterFrame(FilterFrameBase):
|
||||
|
||||
self._b_unknown = gtk.CheckButton("Include Unknown")
|
||||
self._b_unknown.set_sensitive(False)
|
||||
self._b_unknown.set_active(True)
|
||||
self._b_unknown.set_active(False)
|
||||
|
||||
self._birth_check.connect('toggled',lambda b: self._b_edit.set_sensitive(self._birth_check.get_active()))
|
||||
self._birth_check.connect('toggled',lambda b: self._b_before.set_sensitive(self._birth_check.get_active()))
|
||||
self._birth_check.connect('toggled',lambda b: self._b_after.set_sensitive(self._birth_check.get_active()))
|
||||
self._birth_check.connect('toggled',lambda b: self._b_unknown.set_sensitive(self._birth_check.get_active()))
|
||||
|
||||
#self._birth_check.connect('toggled',lambda b: self._b_unknown.set_sensitive(self._birth_check.get_active()))
|
||||
|
||||
self._b_inner_box = gtk.HBox()
|
||||
self._b_inner_box.pack_start(self._b_before)
|
||||
self._b_inner_box.pack_start(self._b_after)
|
||||
@@ -118,12 +118,12 @@ class PersonFilterFrame(FilterFrameBase):
|
||||
|
||||
self._d_unknown = gtk.CheckButton("Include Unknown")
|
||||
self._d_unknown.set_sensitive(False)
|
||||
self._d_unknown.set_active(True)
|
||||
self._d_unknown.set_active(False)
|
||||
|
||||
self._death_check.connect('toggled',lambda b: self._d_edit.set_sensitive(self._death_check.get_active()))
|
||||
self._death_check.connect('toggled',lambda b: self._d_before.set_sensitive(self._death_check.get_active()))
|
||||
self._death_check.connect('toggled',lambda b: self._d_after.set_sensitive(self._death_check.get_active()))
|
||||
self._death_check.connect('toggled',lambda b: self._d_unknown.set_sensitive(self._death_check.get_active()))
|
||||
#self._death_check.connect('toggled',lambda b: self._d_unknown.set_sensitive(self._death_check.get_active()))
|
||||
|
||||
d_inner_box = gtk.HBox()
|
||||
d_inner_box.pack_start(self._d_before)
|
||||
@@ -257,7 +257,68 @@ class PersonFilterFrame(FilterFrameBase):
|
||||
self._table.attach(self._filter_combo,self._control_col,self._control_col+1,
|
||||
current_row,current_row+1,xoptions=gtk.EXPAND|gtk.FILL,yoptions=False)
|
||||
|
||||
def on_apply(self,button):
|
||||
if filter_spec is not None:
|
||||
self._set_filter(filter_spec)
|
||||
|
||||
|
||||
def _set_filter(self,filter_spec):
|
||||
if filter_spec.include_gramps_id():
|
||||
self._id_check.set_active(True)
|
||||
self._id_edit.set_text(filter_spec.get_gramps_id())
|
||||
else:
|
||||
self._id_check.set_active(False)
|
||||
self._id_edit.set_text("")
|
||||
|
||||
if filter_spec.include_name():
|
||||
self._name_check.set_active(True)
|
||||
self._name_edit.set_text(filter_spec.get_name())
|
||||
else:
|
||||
self._name_check.set_active(False)
|
||||
self._name_edit.set_text("")
|
||||
|
||||
if filter_spec.include_gender():
|
||||
self._gender_check.set_active(True)
|
||||
store = self._gender_list
|
||||
it = store.get_iter_first()
|
||||
while it:
|
||||
if store.get(it, 1)[0] == filter_spec.get_gender():
|
||||
break
|
||||
it = store.iter_next(it)
|
||||
|
||||
if it != None:
|
||||
self._gender_combo.set_active_iter(it)
|
||||
else:
|
||||
self._gender_check.set_active(False)
|
||||
|
||||
|
||||
if filter_spec.include_birth():
|
||||
self._birth_check.set_active(True)
|
||||
self._b_edit.set_text(filter_spec.get_birth_year())
|
||||
if filter_spec.get_birth_criteria() == filter_spec.__class__.BEFORE:
|
||||
self._b_before.set_active(True)
|
||||
self._b_after.set_active(False)
|
||||
else:
|
||||
self._b_before.set_active(False)
|
||||
self._b_after.set_active(True)
|
||||
else:
|
||||
self._birth_check.set_active(False)
|
||||
self._b_edit.set_text("")
|
||||
|
||||
if filter_spec.include_death():
|
||||
self._death_check.set_active(True)
|
||||
self._d_edit.set_text(filter_spec.get_death_year())
|
||||
if filter_spec.get_death_criteria() == filter_spec.__class__.BEFORE:
|
||||
self._d_before.set_active(True)
|
||||
self._d_after.set_active(False)
|
||||
else:
|
||||
self._d_before.set_active(False)
|
||||
self._d_after.set_active(True)
|
||||
else:
|
||||
self._death_check.set_active(False)
|
||||
self._d_edit.set_text("")
|
||||
|
||||
|
||||
def on_apply(self,button=None):
|
||||
filter = GenericFilter.GenericFilter()
|
||||
|
||||
if self._id_check.get_active():
|
||||
|
65
src/ObjectSelector/_PersonFilterSpec.py
Normal file
65
src/ObjectSelector/_PersonFilterSpec.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from _FilterSpecBase import FilterSpecBase
|
||||
|
||||
class PersonFilterSpec(FilterSpecBase):
|
||||
|
||||
BEFORE = 1
|
||||
AFTER = 2
|
||||
|
||||
def __init__(self):
|
||||
FilterSpecBase.__init__(self)
|
||||
|
||||
self._name = None
|
||||
self._gender = None
|
||||
self._birth_year = None
|
||||
self._birth_criteria = self.__class__.BEFORE
|
||||
self._death_year = None
|
||||
self._death_criteria = self.__class__.BEFORE
|
||||
|
||||
def set_name(self,name):
|
||||
self._name = name
|
||||
|
||||
def get_name(self):
|
||||
return self._name
|
||||
|
||||
def include_name(self):
|
||||
return self._name is not None
|
||||
|
||||
def set_gender(self,gender):
|
||||
self._gender = gender
|
||||
|
||||
def get_gender(self):
|
||||
return self._gender
|
||||
|
||||
def include_gender(self):
|
||||
return self._gender is not None
|
||||
|
||||
def set_birth_year(self,year):
|
||||
self._birth_year = str(year)
|
||||
|
||||
def get_birth_year(self):
|
||||
return self._birth_year
|
||||
|
||||
def include_birth(self):
|
||||
return self._birth_year is not None
|
||||
|
||||
def set_birth_criteria(self,birth_criteria):
|
||||
self._birth_criteria = birth_criteria
|
||||
|
||||
def get_birth_criteria(self):
|
||||
return self._birth_criteria
|
||||
|
||||
def set_death_year(self,year):
|
||||
self._death_year = str(year)
|
||||
|
||||
def get_death_year(self):
|
||||
return self._death_year
|
||||
|
||||
def include_death(self):
|
||||
return self._death_year is not None
|
||||
|
||||
def set_death_criteria(self,death_criteria):
|
||||
self._death_criteria = death_criteria
|
||||
|
||||
def get_death_criteria(self):
|
||||
return self._death_criteria
|
||||
|
@@ -33,12 +33,13 @@ class PersonFrame(ObjectFrameBase):
|
||||
|
||||
def __init__(self,
|
||||
dbstate,
|
||||
uistate):
|
||||
uistate,
|
||||
filter_spec = None):
|
||||
|
||||
ObjectFrameBase.__init__(self,
|
||||
dbstate=dbstate,
|
||||
uistate=uistate,
|
||||
filter_frame = PersonFilterFrame(dbstate),
|
||||
uistate=uistate,
|
||||
filter_frame = PersonFilterFrame(filter_spec=filter_spec),
|
||||
preview_frame = PersonPreviewFrame(dbstate),
|
||||
tree_frame = PersonTreeFrame(dbstate))
|
||||
|
||||
@@ -59,6 +60,11 @@ class PersonFrame(ObjectFrameBase):
|
||||
|
||||
self._filter_frame.connect('apply-filter',lambda w,m: self._tree_frame.set_model(m))
|
||||
|
||||
# Now that the filter is connected we need to tell it to apply any
|
||||
# filter_spec that may have been passed to it. We can't apply the filter
|
||||
# until the connections have been made.
|
||||
self._filter_frame.on_apply()
|
||||
|
||||
def _on_row_activated(self,widget,path,col):
|
||||
(model, iter) = widget.get_selection().get_selected()
|
||||
if iter and model.get_value(iter,self.__class__.__person_id_field):
|
||||
|
@@ -1,9 +1,22 @@
|
||||
import os.path
|
||||
|
||||
from xml.sax.saxutils import escape
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
from logging import getLogger
|
||||
log = getLogger(".ObjectSelector")
|
||||
|
||||
import ImgManip
|
||||
import const
|
||||
from ToolTips import PersonTip
|
||||
import DateHandler
|
||||
|
||||
def short(val,size=60):
|
||||
if len(val) > size:
|
||||
return "%s..." % val[0:size]
|
||||
else:
|
||||
return val
|
||||
|
||||
class PersonPreviewFrame(gtk.Frame):
|
||||
|
||||
@@ -25,17 +38,14 @@ class PersonPreviewFrame(gtk.Frame):
|
||||
self._image = gtk.Image()
|
||||
|
||||
# test image
|
||||
self._image.set_from_file("../person.svg")
|
||||
self._image.set_from_file(os.path.join(const.rootDir,"person.svg"))
|
||||
|
||||
# Text
|
||||
label = gtk.Label()
|
||||
label.set_use_markup(True)
|
||||
label.set_line_wrap(True)
|
||||
label.set_justify(gtk.JUSTIFY_LEFT)
|
||||
label.set_alignment(xalign=0.1,yalign=0.1)
|
||||
label.set_markup("<b>Name:</b> Joe Blogs\n"
|
||||
"<b>b:</b> 1906\n"
|
||||
"<b>d:</b> 1937\n")
|
||||
label.set_alignment(xalign=0.5,yalign=0.1)
|
||||
|
||||
# box
|
||||
box = gtk.VBox()
|
||||
@@ -56,6 +66,49 @@ class PersonPreviewFrame(gtk.Frame):
|
||||
|
||||
self.add(align)
|
||||
|
||||
self._label = label
|
||||
|
||||
def _get_text_preview(self,person):
|
||||
global escape
|
||||
|
||||
birth_str = ""
|
||||
birth_ref = person.get_birth_ref()
|
||||
if birth_ref:
|
||||
birth = self._dbstate.db.get_event_from_handle(birth_ref.ref)
|
||||
date_str = DateHandler.get_date(birth)
|
||||
if date_str != "":
|
||||
birth_str = escape(date_str)
|
||||
|
||||
death_str = ""
|
||||
death_ref = person.get_death_ref()
|
||||
if death_ref:
|
||||
death = self._dbstate.db.get_event_from_handle(death_ref.ref)
|
||||
date_str = DateHandler.get_date(death)
|
||||
if date_str != "":
|
||||
death_str = escape(date_str)
|
||||
|
||||
s = "<span weight=\"bold\">%s</span>\n"\
|
||||
" <span weight=\"normal\">%s:</span> %s\n"\
|
||||
" <span weight=\"normal\">%s:</span> %s\n"\
|
||||
" <span weight=\"normal\">%s:</span> %s\n"% (
|
||||
_("Person"),
|
||||
_("Name"),escape(person.get_primary_name().get_name()),
|
||||
_("Birth"),birth_str,
|
||||
_("Death"),death_str)
|
||||
|
||||
if len(person.get_source_references()) > 0:
|
||||
psrc_ref = person.get_source_references()[0]
|
||||
psrc_id = psrc_ref.get_base_handle()
|
||||
psrc = self._dbstate.db.get_source_from_handle(psrc_id)
|
||||
|
||||
s += "\n<span weight=\"bold\">%s</span>\n"\
|
||||
" <span weight=\"normal\">%s:</span> %s\n" % (
|
||||
_("Primary source"),
|
||||
_("Name"),
|
||||
escape(short(psrc.get_title())))
|
||||
|
||||
return s
|
||||
|
||||
def set_object_from_id(self,id):
|
||||
try:
|
||||
person = self._dbstate.db.get_person_from_gramps_id(id)
|
||||
@@ -69,14 +122,18 @@ class PersonPreviewFrame(gtk.Frame):
|
||||
pixbuf = ImgManip.get_thumbnail_image(mobj.get_path())
|
||||
self._image.set_from_pixbuf(pixbuf)
|
||||
else:
|
||||
self._image.set_from_file("../person.svg")
|
||||
self._image.set_from_file(os.path.join(const.rootDir,"person.svg"))
|
||||
|
||||
self._label.set_markup(self._get_text_preview(person))
|
||||
|
||||
except:
|
||||
log.warn("Failed to generate preview for person", exc_info=True)
|
||||
self._clear_object()
|
||||
self.clear_object()
|
||||
|
||||
def clear_object(self):
|
||||
self._image.set_from_file("../person.svg")
|
||||
self._image.set_from_file(os.path.join(const.rootDir,"person.svg"))
|
||||
self._label.set_markup("")
|
||||
|
||||
|
||||
if gtk.pygtk_version < (2,8,0):
|
||||
gobject.type_register(PersonPreviewFrame)
|
||||
|
15
src/ObjectSelector/__init__.py
Normal file
15
src/ObjectSelector/__init__.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from gettext import gettext as _
|
||||
|
||||
from _ObjectSelectorWindow import ObjectSelectorWindow
|
||||
from _Constants import ObjectTypes
|
||||
from _PersonFilterSpec import PersonFilterSpec
|
||||
|
||||
class PersonSelector(ObjectSelectorWindow):
|
||||
"""Provides an ObjectSelectorWindow configured for selecting a person object."""
|
||||
|
||||
def __init__(self,dbstate,uistate,track,filter_spec=None,title=_("Select Person")):
|
||||
ObjectSelectorWindow.__init__(self,dbstate,uistate,track,
|
||||
title=title,
|
||||
filter_spec=filter_spec,
|
||||
default_object_type = ObjectTypes.PERSON,
|
||||
object_list = [ObjectTypes.PERSON])
|
Reference in New Issue
Block a user