forked from openlp/openlp
Head1105
This commit is contained in:
commit
eacf6cc870
@ -81,9 +81,6 @@ html_expands.append({u'desc':u'Italics', u'start tag':u'{it}',
|
||||
u'start html':u'<em>', u'end tag':u'{/it}', u'end html':u'</em>',
|
||||
u'protected':True})
|
||||
|
||||
# Image image_cache to stop regualar image resizing
|
||||
image_cache = {}
|
||||
|
||||
def translate(context, text, comment=None):
|
||||
"""
|
||||
A special shortcut method to wrap around the Qt4 translation functions.
|
||||
@ -261,20 +258,14 @@ def resize_image(image, width, height, background=QtCore.Qt.black):
|
||||
return preview
|
||||
preview = preview.scaled(width, height, QtCore.Qt.KeepAspectRatio,
|
||||
QtCore.Qt.SmoothTransformation)
|
||||
image_cache_key = u'%s%s%s' % (image, unicode(width), unicode(height))
|
||||
if image_cache_key in image_cache:
|
||||
log.debug(u'resize_image - end cache')
|
||||
return image_cache[image_cache_key]
|
||||
realw = preview.width()
|
||||
realh = preview.height()
|
||||
# and move it to the centre of the preview space
|
||||
new_image = QtGui.QImage(width, height,
|
||||
QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
new_image.fill(background)
|
||||
painter = QtGui.QPainter(new_image)
|
||||
painter.fillRect(new_image.rect(), background)
|
||||
painter.drawImage((width - realw) / 2, (height - realh) / 2, preview)
|
||||
image_cache[image_cache_key] = new_image
|
||||
log.debug(u'resize_image - end')
|
||||
return new_image
|
||||
|
||||
def check_item_selected(list_widget, message):
|
||||
|
@ -117,6 +117,7 @@ class Manager(object):
|
||||
settings = QtCore.QSettings()
|
||||
settings.beginGroup(plugin_name)
|
||||
self.db_url = u''
|
||||
self.is_dirty = False
|
||||
db_type = unicode(
|
||||
settings.value(u'db type', QtCore.QVariant(u'sqlite')).toString())
|
||||
if db_type == u'sqlite':
|
||||
@ -150,6 +151,7 @@ class Manager(object):
|
||||
self.session.add(object_instance)
|
||||
if commit:
|
||||
self.session.commit()
|
||||
self.is_dirty = True
|
||||
return True
|
||||
except InvalidRequestError:
|
||||
self.session.rollback()
|
||||
@ -220,6 +222,7 @@ class Manager(object):
|
||||
try:
|
||||
self.session.delete(object_instance)
|
||||
self.session.commit()
|
||||
self.is_dirty = True
|
||||
return True
|
||||
except InvalidRequestError:
|
||||
self.session.rollback()
|
||||
@ -241,8 +244,17 @@ class Manager(object):
|
||||
query = query.filter(filter_clause)
|
||||
query.delete(synchronize_session=False)
|
||||
self.session.commit()
|
||||
self.is_dirty = True
|
||||
return True
|
||||
except InvalidRequestError:
|
||||
self.session.rollback()
|
||||
log.exception(u'Failed to delete %s records', object_class.__name__)
|
||||
return False
|
||||
|
||||
def finalise(self):
|
||||
"""
|
||||
VACUUM the database on exit.
|
||||
"""
|
||||
if self.is_dirty:
|
||||
engine = create_engine(self.db_url)
|
||||
engine.execute("vacuum")
|
||||
|
@ -528,6 +528,9 @@ class ThemeXML(object):
|
||||
# make string value unicode
|
||||
if not isinstance(value, unicode):
|
||||
value = unicode(str(value), u'utf-8')
|
||||
# None means an empty string so lets have one.
|
||||
if value == u'None':
|
||||
value = u''
|
||||
setattr(self, tag, unicode(value).strip().lstrip())
|
||||
|
||||
def __str__(self):
|
||||
|
@ -781,7 +781,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
QtGui.QMessageBox.Save),
|
||||
QtGui.QMessageBox.Save)
|
||||
if ret == QtGui.QMessageBox.Save:
|
||||
self.ServiceManagerContents.onSaveService()
|
||||
self.ServiceManagerContents.onSaveService(True)
|
||||
self.cleanUp()
|
||||
event.accept()
|
||||
elif ret == QtGui.QMessageBox.Discard:
|
||||
|
@ -25,6 +25,7 @@
|
||||
###############################################################################
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import translate
|
||||
|
||||
class Ui_ServiceItemEditDialog(object):
|
||||
@ -44,16 +45,26 @@ class Ui_ServiceItemEditDialog(object):
|
||||
self.topLayout.addWidget(self.listWidget)
|
||||
self.buttonLayout = QtGui.QVBoxLayout()
|
||||
self.buttonLayout.setObjectName(u'buttonLayout')
|
||||
self.upButton = QtGui.QPushButton(self.layoutWidget)
|
||||
self.upButton.setObjectName(u'upButton')
|
||||
self.buttonLayout.addWidget(self.upButton)
|
||||
spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum,
|
||||
QtGui.QSizePolicy.Expanding)
|
||||
self.buttonLayout.addItem(spacerItem)
|
||||
self.deleteButton = QtGui.QPushButton(self.layoutWidget)
|
||||
self.deleteButton.setObjectName(u'deleteButton')
|
||||
self.buttonLayout.addWidget(self.deleteButton)
|
||||
spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum,
|
||||
QtGui.QSizePolicy.Expanding)
|
||||
self.buttonLayout.addItem(spacerItem)
|
||||
self.upButton = QtGui.QPushButton(self.layoutWidget)
|
||||
self.upButton.setText(u'')
|
||||
icon = QtGui.QIcon()
|
||||
icon.addPixmap(QtGui.QPixmap(u':/services/service_up.png'),
|
||||
QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.upButton.setIcon(icon)
|
||||
self.upButton.setObjectName(u'upButton')
|
||||
self.buttonLayout.addWidget(self.upButton)
|
||||
self.downButton = QtGui.QPushButton(self.layoutWidget)
|
||||
self.downButton.setText(u'')
|
||||
icon = QtGui.QIcon()
|
||||
icon.addPixmap(QtGui.QPixmap(u':/services/service_down.png'),
|
||||
QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
self.downButton.setIcon(icon)
|
||||
self.downButton.setObjectName(u'downButton')
|
||||
self.buttonLayout.addWidget(self.downButton)
|
||||
self.topLayout.addLayout(self.buttonLayout)
|
||||
@ -70,7 +81,5 @@ class Ui_ServiceItemEditDialog(object):
|
||||
def retranslateUi(self, serviceItemEditDialog):
|
||||
serviceItemEditDialog.setWindowTitle(
|
||||
translate('OpenLP.ServiceItemEditForm', 'Reorder Service Item'))
|
||||
self.upButton.setText(translate('OpenLP.ServiceItemEditForm', 'Up'))
|
||||
self.deleteButton.setText(translate('OpenLP.ServiceItemEditForm',
|
||||
'Delete'))
|
||||
self.downButton.setText(translate('OpenLP.ServiceItemEditForm', 'Down'))
|
||||
|
@ -347,7 +347,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
self.serviceItems[item][u'service_item'])
|
||||
if self.serviceItemEditForm.exec_():
|
||||
self.addServiceItem(self.serviceItemEditForm.getServiceItem(),
|
||||
replace=True, expand=self.serviceItems[item][u'expand'])
|
||||
replace=True, expand=self.serviceItems[item][u'expanded'])
|
||||
|
||||
def nextItem(self):
|
||||
"""
|
||||
@ -651,9 +651,12 @@ class ServiceManager(QtGui.QWidget):
|
||||
.get_service_repr()})
|
||||
if item[u'service_item'].uses_file():
|
||||
for frame in item[u'service_item'].get_frames():
|
||||
path_from = unicode(os.path.join(
|
||||
frame[u'path'],
|
||||
frame[u'title']))
|
||||
if item[u'service_item'].is_image():
|
||||
path_from = frame[u'path']
|
||||
else:
|
||||
path_from = unicode(os.path.join(
|
||||
frame[u'path'],
|
||||
frame[u'title']))
|
||||
# On write a file once
|
||||
if not path_from in write_list:
|
||||
write_list.append(path_from)
|
||||
|
@ -117,3 +117,11 @@ class AlertsPlugin(Plugin):
|
||||
self.textStrings[StringContent.VisibleName] = {
|
||||
u'title': translate('AlertsPlugin', 'Alerts')
|
||||
}
|
||||
|
||||
def finalise(self):
|
||||
"""
|
||||
Time to tidy up on exit
|
||||
"""
|
||||
log.info(u'Alerts Finalising')
|
||||
self.manager.finalise()
|
||||
Plugin.finalise(self)
|
||||
|
@ -172,3 +172,11 @@ class BiblePlugin(Plugin):
|
||||
u'tooltip': translate('BiblesPlugin',
|
||||
'Add the selected Bible to the service')
|
||||
}
|
||||
|
||||
def finalise(self):
|
||||
"""
|
||||
Time to tidy up on exit
|
||||
"""
|
||||
log.info(u'Bible Finalising')
|
||||
self.manager.finalise()
|
||||
Plugin.finalise(self)
|
||||
|
@ -310,3 +310,11 @@ class BibleManager(object):
|
||||
if bible == name:
|
||||
return True
|
||||
return False
|
||||
|
||||
def finalise(self):
|
||||
"""
|
||||
Loop through the databases to VACUUM them.
|
||||
"""
|
||||
for bible in self.db_cache:
|
||||
self.db_cache[bible].finalise()
|
||||
|
||||
|
@ -49,8 +49,8 @@ class CustomPlugin(Plugin):
|
||||
def __init__(self, plugin_helpers):
|
||||
Plugin.__init__(self, u'Custom', u'1.9.3', plugin_helpers)
|
||||
self.weight = -5
|
||||
self.custommanager = Manager(u'custom', init_schema)
|
||||
self.edit_custom_form = EditCustomForm(self.custommanager)
|
||||
self.manager = Manager(u'custom', init_schema)
|
||||
self.edit_custom_form = EditCustomForm(self.manager)
|
||||
self.icon_path = u':/plugins/plugin_custom.png'
|
||||
self.icon = build_icon(self.icon_path)
|
||||
|
||||
@ -59,7 +59,7 @@ class CustomPlugin(Plugin):
|
||||
return CustomTab(self.name, visible_name[u'title'])
|
||||
|
||||
def getMediaManagerItem(self):
|
||||
# Create the CustomManagerItem object
|
||||
# Create the ManagerItem object
|
||||
return CustomMediaItem(self, self, self.icon)
|
||||
|
||||
def about(self):
|
||||
@ -76,7 +76,7 @@ class CustomPlugin(Plugin):
|
||||
|
||||
Returns True if the theme is being used, otherwise returns False.
|
||||
"""
|
||||
if self.custommanager.get_all_objects(CustomSlide,
|
||||
if self.manager.get_all_objects(CustomSlide,
|
||||
CustomSlide.theme_name == theme):
|
||||
return True
|
||||
return False
|
||||
@ -92,11 +92,11 @@ class CustomPlugin(Plugin):
|
||||
``newTheme``
|
||||
The new name the plugin should now use.
|
||||
"""
|
||||
customsUsingTheme = self.custommanager.get_all_objects(CustomSlide,
|
||||
customsUsingTheme = self.manager.get_all_objects(CustomSlide,
|
||||
CustomSlide.theme_name == oldTheme)
|
||||
for custom in customsUsingTheme:
|
||||
custom.theme_name = newTheme
|
||||
self.custommanager.save_object(custom)
|
||||
self.manager.save_object(custom)
|
||||
|
||||
def setPluginTextStrings(self):
|
||||
"""
|
||||
@ -160,3 +160,11 @@ class CustomPlugin(Plugin):
|
||||
u'tooltip': translate('CustomsPlugin',
|
||||
'Add the selected Custom to the service')
|
||||
}
|
||||
|
||||
def finalise(self):
|
||||
"""
|
||||
Time to tidy up on exit
|
||||
"""
|
||||
log.info(u'Custom Finalising')
|
||||
self.manager.finalise()
|
||||
Plugin.finalise(self)
|
||||
|
@ -110,6 +110,7 @@ class Ui_CustomEditDialog(object):
|
||||
self.titleEdit.setObjectName(u'titleEdit')
|
||||
self.horizontalLayout.addWidget(self.titleEdit)
|
||||
self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1)
|
||||
|
||||
self.retranslateUi(customEditDialog)
|
||||
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'accepted()'),
|
||||
customEditDialog.accept)
|
||||
|
@ -41,7 +41,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
||||
Class documentation goes here.
|
||||
"""
|
||||
log.info(u'Custom Editor loaded')
|
||||
def __init__(self, custommanager, parent=None):
|
||||
def __init__(self, manager, parent=None):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
@ -74,7 +74,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'theme_update_list'), self.loadThemes)
|
||||
# Create other objects and forms.
|
||||
self.custommanager = custommanager
|
||||
self.manager = manager
|
||||
self.editSlideForm = EditCustomSlideForm(self)
|
||||
self.initialise()
|
||||
|
||||
@ -115,7 +115,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
||||
self.customSlide = CustomSlide()
|
||||
self.initialise()
|
||||
if id != 0:
|
||||
self.customSlide = self.custommanager.get_object(CustomSlide, id)
|
||||
self.customSlide = self.manager.get_object(CustomSlide, id)
|
||||
self.titleEdit.setText(self.customSlide.title)
|
||||
self.creditEdit.setText(self.customSlide.credits)
|
||||
customXML = CustomXMLParser(self.customSlide.text)
|
||||
@ -168,7 +168,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
||||
u'utf-8')
|
||||
self.customSlide.theme_name = unicode(self.themeComboBox.currentText(),
|
||||
u'utf-8')
|
||||
return self.custommanager.save_object(self.customSlide)
|
||||
return self.manager.save_object(self.customSlide)
|
||||
|
||||
def onUpButtonPressed(self):
|
||||
selectedRow = self.slideListView.currentRow()
|
||||
|
@ -56,6 +56,7 @@ class CustomMediaItem(MediaManagerItem):
|
||||
# Holds information about whether the edit is remotly triggered and
|
||||
# which Custom is required.
|
||||
self.remoteCustom = -1
|
||||
self.manager = parent.manager
|
||||
|
||||
def addEndHeaderBar(self):
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
@ -71,7 +72,7 @@ class CustomMediaItem(MediaManagerItem):
|
||||
MediaManagerItem.requiredIcons(self)
|
||||
|
||||
def initialise(self):
|
||||
self.loadCustomListView(self.parent.custommanager.get_all_objects(
|
||||
self.loadCustomListView(self.manager.get_all_objects(
|
||||
CustomSlide, order_by_ref=CustomSlide.title))
|
||||
#Called to redisplay the song list screen edith from a search
|
||||
#or from the exit of the Song edit dialog. If remote editing is active
|
||||
@ -106,7 +107,7 @@ class CustomMediaItem(MediaManagerItem):
|
||||
type of display is required.
|
||||
"""
|
||||
fields = customid.split(u':')
|
||||
valid = self.parent.custommanager.get_object(CustomSlide, fields[1])
|
||||
valid = self.manager.get_object(CustomSlide, fields[1])
|
||||
if valid:
|
||||
self.remoteCustom = fields[1]
|
||||
self.remoteTriggered = fields[0]
|
||||
@ -139,7 +140,7 @@ class CustomMediaItem(MediaManagerItem):
|
||||
id_list = [(item.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||
for item in self.listView.selectedIndexes()]
|
||||
for id in id_list:
|
||||
self.parent.custommanager.delete_object(CustomSlide, id)
|
||||
self.parent.manager.delete_object(CustomSlide, id)
|
||||
for row in row_list:
|
||||
self.listView.takeItem(row)
|
||||
|
||||
@ -161,7 +162,7 @@ class CustomMediaItem(MediaManagerItem):
|
||||
service_item.add_capability(ItemCapabilities.AllowsEdit)
|
||||
service_item.add_capability(ItemCapabilities.AllowsPreview)
|
||||
service_item.add_capability(ItemCapabilities.AllowsLoop)
|
||||
customSlide = self.parent.custommanager.get_object(CustomSlide, item_id)
|
||||
customSlide = self.parent.manager.get_object(CustomSlide, item_id)
|
||||
title = customSlide.title
|
||||
credit = customSlide.credits
|
||||
service_item.editId = item_id
|
||||
|
@ -56,7 +56,7 @@ class MediaMediaItem(MediaManagerItem):
|
||||
u':/media/media_video.png').toImage()
|
||||
MediaManagerItem.__init__(self, parent, self, icon)
|
||||
self.singleServiceItem = False
|
||||
self.serviceItemIconName = u':/media/media_video.png'
|
||||
self.serviceItemIconName = u':/media/image_clapperboard.png'
|
||||
|
||||
def retranslateUi(self):
|
||||
self.OnNewPrompt = translate('MediaPlugin.MediaItem', 'Select Media')
|
||||
|
@ -199,3 +199,11 @@ class SongsPlugin(Plugin):
|
||||
u'tooltip': translate('SongsPlugin',
|
||||
'Add the selected Song to the service')
|
||||
}
|
||||
|
||||
def finalise(self):
|
||||
"""
|
||||
Time to tidy up on exit
|
||||
"""
|
||||
log.info(u'Songs Finalising')
|
||||
self.manager.finalise()
|
||||
Plugin.finalise(self)
|
||||
|
@ -34,11 +34,11 @@ class SongUsageDeleteForm(QtGui.QDialog, Ui_SongUsageDeleteDialog):
|
||||
"""
|
||||
Class documentation goes here.
|
||||
"""
|
||||
def __init__(self, songusagemanager, parent):
|
||||
def __init__(self, manager, parent):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
self.songusagemanager = songusagemanager
|
||||
self.manager = manager
|
||||
QtGui.QDialog.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
|
||||
@ -53,6 +53,6 @@ class SongUsageDeleteForm(QtGui.QDialog, Ui_SongUsageDeleteDialog):
|
||||
QtGui.QMessageBox.Cancel)
|
||||
if ret == QtGui.QMessageBox.Ok:
|
||||
deleteDate = self.deleteCalendar.selectedDate().toPyDate()
|
||||
self.songusagemanager.delete_all_objects(SongUsageItem,
|
||||
self.manager.delete_all_objects(SongUsageItem,
|
||||
SongUsageItem.usagedate <= deleteDate)
|
||||
self.close()
|
||||
|
@ -76,7 +76,7 @@ class SongUsageDetailForm(QtGui.QDialog, Ui_SongUsageDetailDialog):
|
||||
filename = u'usage_detail_%s_%s.txt' % (
|
||||
self.fromDate.selectedDate().toString(u'ddMMyyyy'),
|
||||
self.toDate.selectedDate().toString(u'ddMMyyyy'))
|
||||
usage = self.plugin.songusagemanager.get_all_objects(
|
||||
usage = self.plugin.manager.get_all_objects(
|
||||
SongUsageItem, and_(
|
||||
SongUsageItem.usagedate >= self.fromDate.selectedDate().toPyDate(),
|
||||
SongUsageItem.usagedate < self.toDate.selectedDate().toPyDate()),
|
||||
|
@ -44,7 +44,7 @@ class SongUsagePlugin(Plugin):
|
||||
Plugin.__init__(self, u'SongUsage', u'1.9.3', plugin_helpers)
|
||||
self.weight = -4
|
||||
self.icon = build_icon(u':/plugins/plugin_songusage.png')
|
||||
self.songusagemanager = None
|
||||
self.manager = None
|
||||
self.songusageActive = False
|
||||
|
||||
def addToolsMenuItem(self, tools_menu):
|
||||
@ -115,9 +115,9 @@ class SongUsagePlugin(Plugin):
|
||||
self.settingsSection + u'/active',
|
||||
QtCore.QVariant(False)).toBool()
|
||||
self.SongUsageStatus.setChecked(self.SongUsageActive)
|
||||
if self.songusagemanager is None:
|
||||
self.songusagemanager = Manager(u'songusage', init_schema)
|
||||
self.SongUsagedeleteform = SongUsageDeleteForm(self.songusagemanager,
|
||||
if self.manager is None:
|
||||
self.manager = Manager(u'songusage', init_schema)
|
||||
self.SongUsagedeleteform = SongUsageDeleteForm(self.manager,
|
||||
self.formparent)
|
||||
self.SongUsagedetailform = SongUsageDetailForm(self, self.formparent)
|
||||
self.SongUsageMenu.menuAction().setVisible(True)
|
||||
@ -148,7 +148,7 @@ class SongUsagePlugin(Plugin):
|
||||
song_usage_item.authors = u''
|
||||
for author in audit[1]:
|
||||
song_usage_item.authors += author + u' '
|
||||
self.songusagemanager.save_object(song_usage_item)
|
||||
self.manager.save_object(song_usage_item)
|
||||
|
||||
def onSongUsageDelete(self):
|
||||
self.SongUsagedeleteform.exec_()
|
||||
@ -176,3 +176,11 @@ class SongUsagePlugin(Plugin):
|
||||
self.textStrings[StringContent.VisibleName] = {
|
||||
u'title': translate('SongUsagePlugin', 'SongUsage')
|
||||
}
|
||||
|
||||
def finalise(self):
|
||||
"""
|
||||
Time to tidy up on exit
|
||||
"""
|
||||
log.info(u'SongUsage Finalising')
|
||||
self.manager.finalise()
|
||||
Plugin.finalise(self)
|
||||
|
@ -35,9 +35,9 @@
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="buttonLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="upButton">
|
||||
<widget class="QPushButton" name="deleteButton">
|
||||
<property name="text">
|
||||
<string>Up</string>
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -55,16 +55,24 @@
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="deleteButton">
|
||||
<widget class="QPushButton" name="upButton">
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../images/openlp-2.qrc">
|
||||
<normaloff>:/services/service_up.png</normaloff>:/services/service_up.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="downButton">
|
||||
<property name="text">
|
||||
<string>Down</string>
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../images/openlp-2.qrc">
|
||||
<normaloff>:/services/service_down.png</normaloff>:/services/service_down.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -82,6 +90,8 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<resources>
|
||||
<include location="../images/openlp-2.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
Loading…
Reference in New Issue
Block a user