Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dev.gajim.org/gajim/gajim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/gajim-history-manager.186
-rw-r--r--gajim/data/gui/application_menu.ui4
-rw-r--r--gajim/data/gui/history_manager.ui257
-rw-r--r--gajim/gtk/application.py6
-rw-r--r--gajim/gtk/builder.pyi21
-rw-r--r--gajim/gtk/const.py1
-rw-r--r--gajim/history_manager.py716
-rwxr-xr-xlaunch-history-manager.py4
-rw-r--r--setup.py3
-rw-r--r--win/_base.sh2
10 files changed, 2 insertions, 1098 deletions
diff --git a/data/gajim-history-manager.1 b/data/gajim-history-manager.1
deleted file mode 100644
index 14f8d6058..000000000
--- a/data/gajim-history-manager.1
+++ /dev/null
@@ -1,86 +0,0 @@
-.Dd January 21, 2018
-.Dt GAJIM-HISTORY-MANAGER 1 URM
-.Os UNIX
-.Sh NAME
-.Nm gajim-history-manager
-.Nd a tool to manage
-.Xr gajim 1
-logs
-.Sh SYNOPSIS
-.Nm
-.Fl h
-.Nm
-.Op Fl c Ar directory
-.Sh DESCRIPTION
-.Nm
-is a tool to manage
-.Po do some cleanup Pc log file of
-.Xr gajim 1 .
-Use this program to delete or export logs.
-For more information on database logs see <https://trac.gajim.org/wiki/LogsDatabase>.
-.Sh OPTIONS
-.Bl -tag -width Ds
-.It Fl h Fl Fl help
-Show help options
-.It Fl c Fl Fl config-path Em directory
-Where to look for logs file
-.El
-.Sh FILES
-.Bl -tag -width Ds
-.It $XDG_DATA_HOME/gajim/logs.db
-The history database log file used when
-.Op Fl c
-is not specified.
-.El
-.Sh AUTHORS
-.An -nosplit
-.Nm
-is written and maintained by
-.An Yann Leboulanger ,
-and
-.An Denis Fomin ,
-with contributions and patches merged from many individuals around the world.
-See files
-.Pa AUTHORS
-and
-.Pa THANKS ,
-for a complete list.
-.Sh COPYRIGHT
-Copyright (C) 2003-2022 Gajim Team
-.Pp
-.Nm
-is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 3 only.
-.Pp
-.Nm
-is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-.Pp
-You should have received a copy of the GNU General Public License along with
-.Nm .
-If not, see <https://www.gnu.org/licenses/>.
-.Sh FEEDBACK
-You can report bugs or feature requests in our bug tracker at
-.Em https://dev.gajim.org/gajim/gajim/issues
-or in the
-.Em gajim-devel
-mailing list; if you want to send us a patch, please do so in our bug tracker.
-You can also find us in our chat room.
-.Sh WWW
-https://www.gajim.org/
-.Sh XMPP
-You are welcome to join us at gajim@conference.gajim.org
-.Sh MAILING LIST
-Below are public mailing lists on lists.gajim.org
-.Bd -literal -offset indent
-https://lists.gajim.org/cgi-bin/listinfo/gajim-devel
-https://lists.gajim.org/cgi-bin/listinfo/translators
-.Ed
-.Pp
-More mailing lists at
-.Bd -literal -offset indent
-https://lists.gajim.org/cgi-bin/listinfo
-.Ed
-.Sh BUGS
-Please submit bugs at https://dev.gajim.org/gajim/gajim/issues
-.Sh SEE ALSO
-.Xr gajim 1
-.Xr gajim-remote 1
diff --git a/gajim/data/gui/application_menu.ui b/gajim/data/gui/application_menu.ui
index 58705418b..11c00b5b4 100644
--- a/gajim/data/gui/application_menu.ui
+++ b/gajim/data/gui/application_menu.ui
@@ -15,10 +15,6 @@
<attribute name="target"></attribute>
</item>
<item>
- <attribute name="label" translatable="yes">_History Manager</attribute>
- <attribute name="action">app.history-manager</attribute>
- </item>
- <item>
<attribute name="label" translatable="yes">Pl_ugins</attribute>
<attribute name="action">app.plugins</attribute>
</item>
diff --git a/gajim/data/gui/history_manager.ui b/gajim/data/gui/history_manager.ui
deleted file mode 100644
index d24942c13..000000000
--- a/gajim/data/gui/history_manager.ui
+++ /dev/null
@@ -1,257 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.36.0 -->
-<interface>
- <requires lib="gtk+" version="3.22"/>
- <object class="GtkMenu" id="context_menu">
- <property name="can_focus">False</property>
- <child>
- <object class="GtkMenuItem" id="export_menuitem">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Export</property>
- <property name="use_underline">True</property>
- <signal name="activate" handler="on_export_menuitem_activate" swapped="no"/>
- </object>
- </child>
- <child>
- <object class="GtkMenuItem" id="delete_menuitem">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Delete</property>
- <property name="use_underline">True</property>
- </object>
- </child>
- </object>
- <object class="GtkWindow" id="history_manager_window">
- <property name="width_request">1000</property>
- <property name="height_request">500</property>
- <property name="can_focus">False</property>
- <property name="title" translatable="yes">Gajim History Logs Manager</property>
- <property name="window_position">center</property>
- <property name="default_width">1000</property>
- <property name="default_height">500</property>
- <signal name="delete-event" handler="on_history_manager_window_delete_event" swapped="no"/>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="border_width">18</property>
- <property name="orientation">vertical</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkPaned">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="position">250</property>
- <child>
- <object class="GtkScrolledWindow" id="jids_scrolledwindow">
- <property name="width_request">250</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkTreeView" id="jids_listview">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <signal name="button-press-event" handler="on_listview_button_press_event" swapped="no"/>
- <signal name="key-press-event" handler="on_jids_listview_key_press_event" swapped="no"/>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="treeview-selection1"/>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="resize">False</property>
- <property name="shrink">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkBox" id="welcome_box">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_start">20</property>
- <property name="margin_end">20</property>
- <property name="orientation">vertical</property>
- <property name="spacing">10</property>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Gajim History Logs Manager</property>
- <property name="use_markup">True</property>
- <property name="xalign">0.5</property>
- <style>
- <class name="large-header"/>
- </style>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">This history manager is not intended for viewing chat history. If you are looking for such functionality, please use the history window instead.
-
-You can use this program to delete or export history. Either select logs from the left or search the database.</property>
- <property name="wrap">True</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">&lt;b&gt;WARNING:&lt;/b&gt;
-If you plan to do massive deletions, please make sure Gajim is not running. Generally avoid deletions with contacts you currently chat with.</property>
- <property name="use_markup">True</property>
- <property name="wrap">True</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkScrolledWindow" id="logs_scrolledwindow">
- <property name="can_focus">True</property>
- <property name="no_show_all">True</property>
- <property name="margin_start">6</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkTreeView" id="logs_listview">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="enable_search">False</property>
- <signal name="button-press-event" handler="on_listview_button_press_event" swapped="no"/>
- <signal name="key-press-event" handler="on_logs_listview_key_press_event" swapped="no"/>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="treeview-selection2"/>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkScrolledWindow" id="search_results_scrolledwindow">
- <property name="can_focus">True</property>
- <property name="no_show_all">True</property>
- <property name="margin_start">6</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkTreeView" id="search_results_listview">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <signal name="button-press-event" handler="on_listview_button_press_event" swapped="no"/>
- <signal name="row-activated" handler="on_search_results_listview_row_activated" swapped="no"/>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="treeview-selection3"/>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="resize">True</property>
- <property name="shrink">True</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkEntry" id="search_entry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="activates_default">True</property>
- <property name="placeholder_text" translatable="yes">Search database...</property>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="search_db_button">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="has_default">True</property>
- <property name="receives_default">False</property>
- <property name="tooltip_text" translatable="yes">Search in database</property>
- <property name="use_underline">True</property>
- <property name="image_position">bottom</property>
- <signal name="clicked" handler="on_search_db_button_clicked" swapped="no"/>
- <child>
- <object class="GtkImage">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">edit-find-symbolic</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- </child>
- <child type="titlebar">
- <placeholder/>
- </child>
- </object>
-</interface>
diff --git a/gajim/gtk/application.py b/gajim/gtk/application.py
index 1f223ae8c..9e9387e77 100644
--- a/gajim/gtk/application.py
+++ b/gajim/gtk/application.py
@@ -379,7 +379,6 @@ class GajimApplication(Gtk.Application, CoreApplication):
('quit', None, self._on_quit_action),
('add-account', None, self._on_add_account_action),
('manage-proxies', None, self._on_manage_proxies_action),
- ('history-manager', None, self._on_history_manager_action),
('preferences', None, self._on_preferences_action),
('plugins', None, self._on_plugins_action),
('xml-console', None, self._on_xml_console_action),
@@ -546,11 +545,6 @@ class GajimApplication(Gtk.Application, CoreApplication):
window.select_account(account)
@staticmethod
- def _on_history_manager_action(_action: Gio.SimpleAction,
- _param: Optional[GLib.Variant]) -> None:
- open_window('HistoryManager')
-
- @staticmethod
def _on_bookmarks_action(_action: Gio.SimpleAction,
param: GLib.Variant) -> None:
account = param.get_string()
diff --git a/gajim/gtk/builder.pyi b/gajim/gtk/builder.pyi
index 4f4e7bc52..29770fd7d 100644
--- a/gajim/gtk/builder.pyi
+++ b/gajim/gtk/builder.pyi
@@ -513,25 +513,6 @@ class GroupchatControlBuilder(Builder):
visitor_popover: Gtk.Popover
-class HistoryManagerBuilder(Builder):
- context_menu: Gtk.Menu
- export_menuitem: Gtk.MenuItem
- delete_menuitem: Gtk.MenuItem
- history_manager_window: Gtk.Window
- jids_scrolledwindow: Gtk.ScrolledWindow
- jids_listview: Gtk.TreeView
- treeview_selection1: Gtk.TreeSelection
- welcome_box: Gtk.Box
- logs_scrolledwindow: Gtk.ScrolledWindow
- logs_listview: Gtk.TreeView
- treeview_selection2: Gtk.TreeSelection
- search_results_scrolledwindow: Gtk.ScrolledWindow
- search_results_listview: Gtk.TreeView
- treeview_selection3: Gtk.TreeSelection
- search_entry: Gtk.Entry
- search_db_button: Gtk.Button
-
-
class XmlConsoleBuilder(Builder):
headerbar: Gtk.HeaderBar
search_toggle: Gtk.ToggleButton
@@ -1055,8 +1036,6 @@ def get_builder(file_name: Literal['main.ui'], widgets: list[str] = ...) -> Main
@overload
def get_builder(file_name: Literal['groupchat_control.ui'], widgets: list[str] = ...) -> GroupchatControlBuilder: ...
@overload
-def get_builder(file_name: Literal['history_manager.ui'], widgets: list[str] = ...) -> HistoryManagerBuilder: ...
-@overload
def get_builder(file_name: Literal['xml_console.ui'], widgets: list[str] = ...) -> XmlConsoleBuilder: ...
@overload
def get_builder(file_name: Literal['ssl_error_dialog.ui'], widgets: list[str] = ...) -> SslErrorDialogBuilder: ...
diff --git a/gajim/gtk/const.py b/gajim/gtk/const.py
index 0ac3a015c..bfc97442c 100644
--- a/gajim/gtk/const.py
+++ b/gajim/gtk/const.py
@@ -145,7 +145,6 @@ WINDOW_MODULES = {
'XMLConsoleWindow': 'gajim.gui.xml_console',
'GroupchatJoin': 'gajim.gui.groupchat_join',
'PEPConfig': 'gajim.gui.pep_config',
- 'HistoryManager': 'gajim.history_manager',
'GroupchatConfig': 'gajim.gui.groupchat_config',
'ProfileWindow': 'gajim.gui.profile',
'SSLErrorDialog': 'gajim.gui.ssl_error_dialog',
diff --git a/gajim/history_manager.py b/gajim/history_manager.py
deleted file mode 100644
index 3be885e6a..000000000
--- a/gajim/history_manager.py
+++ /dev/null
@@ -1,716 +0,0 @@
-# Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
-# Copyright (C) 2006-2007 Jean-Marie Traissard <jim AT lapin.org>
-# Nikos Kouremenos <kourem AT gmail.com>
-# Copyright (C) 2006-2014 Yann Leboulanger <asterix AT lagaule.org>
-# Copyright (C) 2007 Stephan Erb <steve-e AT h3c.de>
-# Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
-# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
-#
-# This file is part of Gajim.
-#
-# Gajim is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published
-# by the Free Software Foundation; version 3 only.
-#
-# Gajim is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
-
-# NOTE: some method names may match those of logger.py but that's it
-# someday (TM) should have common class
-# that abstracts db connections and helpers on it
-# the same can be said for history.py
-
-from typing import List
-
-import os
-import sys
-import time
-import getopt
-import sqlite3
-from enum import IntEnum, unique
-
-import gi
-
-try:
- gi.require_versions({'Gtk': '3.0'})
-except ValueError as error:
- sys.exit('Missing dependency: %s' % error)
-
-# pylint: disable=C0413
-from gi.repository import Gtk
-from gi.repository import Gdk
-from gi.repository import GLib
-from gi.repository import Gio
-from gi.repository import Pango
-
-import gajim.gui
-from gajim.common import app
-from gajim.common import configpaths
-from gajim.common.i18n import _
-from gajim.common.i18n import ngettext
-from gajim.common.const import StyleAttr
-from gajim.common.const import JIDConstant
-from gajim.common.const import KindConstant
-from gajim.common.const import ShowConstant
-
-
-def is_standalone() -> bool:
- # Determine if we are in standalone mode
- if Gio.Application.get_default() is None:
- return True
- if __name__ == '__main__':
- return True
- return False
-
-
-def init_gtk() -> None:
- gajim.gui.init('gtk')
- from gajim.gtk import exception
- exception.init()
-
-
-if is_standalone():
- init_gtk()
-
- from gajim.common.settings import Settings
-
- try:
- shortargs = 'hvsc:l:p:'
- longargs = 'help verbose separate config-path= loglevel= profile='
- opts = getopt.getopt(sys.argv[1:], shortargs, longargs.split())[0]
- except getopt.error as msg:
- print(str(msg))
- print('for help use --help')
- sys.exit(2)
- for o, a in opts:
- if o in ('-h', '--help'):
- print(_('Usage:') + \
- '\n gajim-history-manager [options] filename\n\n' + \
- _('Options:') + \
- '\n -h, --help ' + \
- _('Show this help message and exit') + \
- '\n -c, --config-path ' + _('Choose folder for logfile') + \
- '\n')
- sys.exit()
- elif o in ('-c', '--config-path'):
- configpaths.set_config_root(a)
-
- configpaths.init()
- app.settings = Settings()
- app.settings.init()
- app.load_css_config()
-else:
- from gajim.common.settings import Settings
-
-from gajim.common import helpers
-from gajim.gui.dialogs import ErrorDialog
-from gajim.gui.dialogs import ConfirmationDialog
-from gajim.gui.dialogs import DialogButton
-from gajim.gui.filechoosers import FileSaveDialog
-from gajim.gui.util import convert_rgb_to_hex
-from gajim.gui.builder import get_builder
-from gajim.gui.util import get_app_icon_list
-# pylint: enable=C0413
-
-@unique
-class Column(IntEnum):
- UNIXTIME = 2
- MESSAGE = 3
- SUBJECT = 4
- NICKNAME = 5
-
-
-class HistoryManager:
- def __init__(self) -> None:
- log_db_path = configpaths.get('LOG_DB')
- if not log_db_path.exists():
- ErrorDialog(_('Cannot find history logs database'),
- _('%s does not exist.') % log_db_path)
- sys.exit()
-
- self._ui = get_builder('history_manager.ui')
- Gtk.Window.set_default_icon_list(get_app_icon_list(
- self._ui.history_manager_window))
-
- # holds jids that we already have in DB
- self.jids_already_in: List[str] = []
- self.AT_LEAST_ONE_DELETION_DONE = False
-
- self.con = sqlite3.connect(
- log_db_path, timeout=20.0, isolation_level='IMMEDIATE')
- self.con.execute("PRAGMA secure_delete=1")
- self.cur = self.con.cursor()
-
- self._init_jids_listview()
- self._init_logs_listview()
- self._init_search_results_listview()
-
- self._fill_jids_listview()
-
- self._ui.search_entry.grab_focus()
-
- self._ui.history_manager_window.show_all()
-
- self._ui.connect_signals(self)
-
- def _init_jids_listview(self) -> None:
- self.jids_liststore = Gtk.ListStore(str, str) # jid, jid_id
- self._ui.jids_listview.set_model(self.jids_liststore)
- self._ui.jids_listview.get_selection().set_mode(
- Gtk.SelectionMode.MULTIPLE)
-
- renderer_text = Gtk.CellRendererText() # holds jid
- col = Gtk.TreeViewColumn(_('XMPP Address'), renderer_text, text=0)
- self._ui.jids_listview.append_column(col)
-
- self._ui.jids_listview.get_selection().connect('changed',
- self.on_jids_listview_selection_changed)
-
- def _init_logs_listview(self) -> None:
- # log_line_id(HIDDEN), jid_id(HIDDEN), time, message, subject, nickname
- self.logs_liststore = Gtk.ListStore(str, str, str, str, str, str)
- self._ui.logs_listview.set_model(self.logs_liststore)
- self._ui.logs_listview.get_selection().set_mode(
- Gtk.SelectionMode.MULTIPLE)
-
- renderer_text = Gtk.CellRendererText() # holds time
- col = Gtk.TreeViewColumn(
- _('Date'), renderer_text, text=Column.UNIXTIME)
- # user can click this header and sort
- col.set_sort_column_id(Column.UNIXTIME)
- col.set_resizable(True)
- self._ui.logs_listview.append_column(col)
-
- renderer_text = Gtk.CellRendererText() # holds nickname
- col = Gtk.TreeViewColumn(
- _('Nickname'), renderer_text, text=Column.NICKNAME)
- # user can click this header and sort
- col.set_sort_column_id(Column.NICKNAME)
- col.set_resizable(True)
- col.set_visible(False)
- self.nickname_col_for_logs = col
- self._ui.logs_listview.append_column(col)
-
- renderer_text = Gtk.CellRendererText() # holds message
- renderer_text.set_property('width_chars', 60)
- renderer_text.set_property('ellipsize', Pango.EllipsizeMode.END)
- col = Gtk.TreeViewColumn(
- _('Message'), renderer_text, markup=Column.MESSAGE)
- # user can click this header and sort
- col.set_sort_column_id(Column.MESSAGE)
- col.set_resizable(True)
- self.message_col_for_logs = col
- self._ui.logs_listview.append_column(col)
-
- renderer_text = Gtk.CellRendererText() # holds subject
- col = Gtk.TreeViewColumn(
- _('Subject'), renderer_text, text=Column.SUBJECT)
- # user can click this header and sort
- col.set_sort_column_id(Column.SUBJECT)
- col.set_resizable(True)
- col.set_visible(False)
- self.subject_col_for_logs = col
- self._ui.logs_listview.append_column(col)
-
- def _init_search_results_listview(self) -> None:
- # log_line_id (HIDDEN), jid, time, message, subject, nickname
- self.search_results_liststore = Gtk.ListStore(
- int, str, str, str, str, str)
- self._ui.search_results_listview.set_model(
- self.search_results_liststore)
-
- renderer_text = Gtk.CellRendererText() # holds JID (who said this)
- col = Gtk.TreeViewColumn(_('XMPP Address'), renderer_text, text=1)
- # user can click this header and sort
- col.set_sort_column_id(1)
- col.set_resizable(True)
- self._ui.search_results_listview.append_column(col)
-
- renderer_text = Gtk.CellRendererText() # holds time
- col = Gtk.TreeViewColumn(
- _('Date'), renderer_text, text=Column.UNIXTIME)
- # user can click this header and sort
- col.set_sort_column_id(Column.UNIXTIME)
- col.set_resizable(True)
- self._ui.search_results_listview.append_column(col)
-
- renderer_text = Gtk.CellRendererText() # holds message
- renderer_text.set_property('width_chars', 60)
- renderer_text.set_property('ellipsize', Pango.EllipsizeMode.END)
- col = Gtk.TreeViewColumn(
- _('Message'), renderer_text, text=Column.MESSAGE)
- # user can click this header and sort
- col.set_sort_column_id(Column.MESSAGE)
- col.set_resizable(True)
- self._ui.search_results_listview.append_column(col)
-
- renderer_text = Gtk.CellRendererText() # holds subject
- col = Gtk.TreeViewColumn(
- _('Subject'), renderer_text, text=Column.SUBJECT)
- # user can click this header and sort
- col.set_sort_column_id(Column.SUBJECT)
- col.set_resizable(True)
- self._ui.search_results_listview.append_column(col)
-
- renderer_text = Gtk.CellRendererText() # holds nickname
- col = Gtk.TreeViewColumn(
- _('Nickname'), renderer_text, text=Column.NICKNAME)
- # user can click this header and sort
- col.set_sort_column_id(Column.NICKNAME)
- col.set_resizable(True)
- self._ui.search_results_listview.append_column(col)
-
- def on_history_manager_window_delete_event(self, _widget, _event):
- if not self.AT_LEAST_ONE_DELETION_DONE:
- if is_standalone():
- Gtk.main_quit()
- return
-
- def _on_yes():
- self.cur.execute('VACUUM')
- self.con.commit()
- if is_standalone():
- Gtk.main_quit()
-
- def _on_no():
- if is_standalone():
- Gtk.main_quit()
-
- ConfirmationDialog(
- _('Database Cleanup'),
- _('Clean up the database?'),
- _('This is STRONGLY NOT RECOMMENDED IF GAJIM IS RUNNING.\n'
- 'Normally, the allocated database size will not be freed, it '
- 'will just become reusable. This operation may take a while.'),
- [DialogButton.make('Cancel',
- callback=_on_no),
- DialogButton.make('Remove',
- text=_('_Cleanup'),
- callback=_on_yes)]).show()
-
- def _fill_jids_listview(self) -> None:
- # get those jids that have at least one entry in logs
- self.cur.execute('SELECT jid, jid_id FROM jids WHERE jid_id IN ('
- 'SELECT distinct logs.jid_id FROM logs) ORDER BY jid')
- # list of tuples: [('aaa@bbb',), ('cc@dd',)]
- rows = self.cur.fetchall()
- for row in rows:
- self.jids_already_in.append(row[0]) # jid
- self.jids_liststore.append([row[0], str(row[1])]) # jid, jid_id
-
- def on_jids_listview_selection_changed(self, _widget):
- selection = self._ui.jids_listview.get_selection()
- liststore, list_of_paths = selection.get_selected_rows()
-
- self.logs_liststore.clear()
- if not list_of_paths:
- return
-
- self._ui.welcome_box.hide()
- self._ui.search_results_scrolledwindow.hide()
- self._ui.logs_scrolledwindow.show()
-
- list_of_rowrefs = []
- for path in list_of_paths: # make them treerowrefs (it's needed)
- list_of_rowrefs.append(Gtk.TreeRowReference.new(liststore, path))
-
- for rowref in list_of_rowrefs: # FILL THE STORE, for all rows selected
- path = rowref.get_path()
- if path is None:
- continue
- jid = liststore[path][0] # jid
- self._fill_logs_listview(jid)
-
- def _get_jid_id(self, jid: str) -> str:
- """
- jids table has jid and jid_id
- logs table has log_id, jid_id, contact_name, time, kind, show, message
-
- So to ask logs we need jid_id that matches our jid in jids table this
- method wants jid and returns the jid_id for later sql-ing on logs
- """
- if jid.find('/') != -1: # if it has a /
- jid_is_from_pm = self._jid_is_from_pm(jid)
- if not jid_is_from_pm: # it's normal jid with resource
- jid = jid.split('/', 1)[0] # remove the resource
- self.cur.execute('SELECT jid_id FROM jids WHERE jid = ?', (jid,))
- jid_id = self.cur.fetchone()[0]
- return str(jid_id)
-
- def _get_jid_from_jid_id(self, jid_id: str) -> str:
- """
- jids table has jid and jid_id
-
- This method accepts jid_id and returns the jid for later sql-ing on logs
- """
- self.cur.execute('SELECT jid FROM jids WHERE jid_id = ?', (jid_id,))
- jid = self.cur.fetchone()[0]
- return jid
-
- def _jid_is_from_pm(self, jid: str) -> bool:
- """
- If jid is gajim@conf/nkour it's likely a pm one, how we know gajim@conf
- is not a normal guy and nkour is not his resource? We ask if gajim@conf
- is already in jids (with type room jid). This fails if user disables
- logging for room and only enables for pm (so highly unlikely) and if we
- fail we do not go chaos (user will see the first pm as if it was message
- in room's public chat) and after that everything is ok
- """
- possible_room_jid = jid.split('/', 1)[0]
-
- self.cur.execute('SELECT jid_id FROM jids WHERE jid = ? AND type = ?',
- (possible_room_jid, JIDConstant.ROOM_TYPE))
- row = self.cur.fetchone()
- if row is None:
- return False
- return True
-
- def _jid_is_room_type(self, jid: str) -> bool:
- """
- Return True/False if given id is room type or not eg. if it is room
- """
- self.cur.execute('SELECT type FROM jids WHERE jid = ?', (jid,))
- row = self.cur.fetchone()
- return row[0] == JIDConstant.ROOM_TYPE
-
- def _fill_logs_listview(self, jid: str) -> None:
- """
- Fill the listview with all messages that user sent to or received from
- JID
- """
- # no need to lower jid in this context as jid is already lowered
- # as we use those jids from db
- jid_id = self._get_jid_id(jid)
- self.cur.execute('''
- SELECT log_line_id, jid_id, time, kind, message, subject, contact_name, show
- FROM logs
- WHERE jid_id = ?
- ORDER BY time
- ''', (jid_id,))
-
- results = self.cur.fetchall()
-
- if self._jid_is_room_type(jid): # is it room?
- self.nickname_col_for_logs.set_visible(True)
- self.subject_col_for_logs.set_visible(False)
- else:
- self.nickname_col_for_logs.set_visible(False)
- self.subject_col_for_logs.set_visible(True)
-
- format_ = helpers.from_one_line(app.settings.get('time_stamp'))
- for row in results:
- # exposed in UI (TreeViewColumns) are only
- # time, message, subject, nickname
- # but store in liststore
- # log_line_id, jid_id, time, message, subject, nickname
- log_line_id, jid_id, time_, kind, message, subject, nickname, \
- show = row
- try:
- time_ = time.strftime(format_, time.localtime(float(time_)))
- except ValueError:
- pass
- else:
- color = None
- if kind in (KindConstant.SINGLE_MSG_RECV,
- KindConstant.CHAT_MSG_RECV,
- KindConstant.GC_MSG):
- color = app.css_config.get_value(
- '.gajim-incoming-nickname', StyleAttr.COLOR)
- elif kind in (KindConstant.SINGLE_MSG_SENT,
- KindConstant.CHAT_MSG_SENT):
- color = app.css_config.get_value(
- '.gajim-outgoing-nickname', StyleAttr.COLOR)
- elif kind in (KindConstant.STATUS,
- KindConstant.GCSTATUS):
- color = app.css_config.get_value(
- '.gajim-status-message', StyleAttr.COLOR)
- # include status into (status) message
- if message is None:
- message = ''
- else:
- message = ' : ' + message
-
- message = helpers.get_uf_show(ShowConstant(show)) + message
-
- message_ = '<span'
- if color:
- message_ += f' foreground="{convert_rgb_to_hex(color)}"'
- message_ += f'>{GLib.markup_escape_text(message)}</span>'
- self.logs_liststore.append(
- (str(log_line_id), str(jid_id),
- time_, message_, subject, nickname))
-
- def _fill_search_results_listview(self, text: str) -> None:
- """
- Ask db and fill listview with results that match text
- """
- self.search_results_liststore.clear()
- like_sql = '%' + text + '%'
- self.cur.execute('''
- SELECT log_line_id, jid_id, time, message, subject, contact_name
- FROM logs
- WHERE message LIKE ? OR subject LIKE ?
- ORDER BY time
- ''', (like_sql, like_sql))
-
- results = self.cur.fetchall()
- format_ = helpers.from_one_line(app.settings.get('time_stamp'))
- for row in results:
- # exposed in UI (TreeViewColumns) are only
- # JID, time, message, subject, nickname
- # but store in liststore
- # log_line_id, jid (from jid_id), time, message, subject, nickname
- log_line_id, jid_id, time_, message, subject, nickname = row
- try:
- time_ = time.strftime(format_, time.localtime(float(time_)))
- except ValueError:
- pass
- else:
- jid = self._get_jid_from_jid_id(jid_id)
-
- self.search_results_liststore.append((log_line_id, jid, time_,
- message, subject, nickname))
-
- def on_logs_listview_key_press_event(self, _widget, event):
- liststore, list_of_paths = self._ui.logs_listview.get_selection()\
- .get_selected_rows()
- if event.keyval == Gdk.KEY_Delete:
- self._delete_logs(liststore, list_of_paths)
-
- def on_listview_button_press_event(self, widget, event):
- if event.button == 3: # right click
- _ui = get_builder('history_manager.ui', ['context_menu'])
- if Gtk.Buildable.get_name(widget) != 'jids_listview':
- _ui.export_menuitem.hide()
- _ui.delete_menuitem.connect('activate',
- self.on_delete_menuitem_activate, widget)
-
- _ui.connect_signals(self)
- _ui.context_menu.popup(None, None, None, None,
- event.button, event.time)
- return True
-
- def on_export_menuitem_activate(self, _widget):
- FileSaveDialog(self._on_export,
- transient_for=self._ui.history_manager_window,
- modal=True)
-
- def _on_export(self, filename: str) -> None:
- selection = self._ui.jids_listview.get_selection()
- liststore, list_of_paths = selection.get_selected_rows()
- self._export_jids_logs_to_file(liststore, list_of_paths, filename)
-
- def on_delete_menuitem_activate(self, _widget, listview):
- widget_name = Gtk.Buildable.get_name(listview)
- liststore, list_of_paths = listview.get_selection().get_selected_rows()
- if widget_name == 'jids_listview':
- self._delete_jid_logs(liststore, list_of_paths)
- elif widget_name in ('logs_listview', 'search_results_listview'):
- self._delete_logs(liststore, list_of_paths)
- else: # Huh ? We don't know this widget
- return
-
- def on_jids_listview_key_press_event(self, _widget, event):
- liststore, list_of_paths = self._ui.jids_listview.get_selection()\
- .get_selected_rows()
- if event.keyval == Gdk.KEY_Delete:
- self._delete_jid_logs(liststore, list_of_paths)
-
- def _export_jids_logs_to_file(self, liststore, list_of_paths, path_to_file):
- paths_len = len(list_of_paths)
- if paths_len == 0: # nothing is selected
- return
-
- list_of_rowrefs = []
- for path in list_of_paths: # make them treerowrefs (it's needed)
- list_of_rowrefs.append(Gtk.TreeRowReference.new(liststore, path))
-
- for rowref in list_of_rowrefs:
- path = rowref.get_path()
- if path is None:
- continue
- jid_id = liststore[path][1]
- self.cur.execute('''
- SELECT time, kind, message, contact_name FROM logs
- WHERE jid_id = ?
- ORDER BY time
- ''', (jid_id,))
-
- # FIXME: we may have two contacts selected to export. fix that
- # AT THIS TIME FIRST EXECUTE IS LOST! WTH!!!!!
- results = self.cur.fetchall()
- #print results[0]
- with open(path_to_file, 'w', encoding='utf-8') as file_:
- for row in results:
- # in store: time, kind, message, contact_name FROM logs
- # in text: JID or You or nickname (if it's gc_msg), time,
- # message
- time_, kind, message, nickname = row
- if kind in (KindConstant.SINGLE_MSG_RECV,
- KindConstant.CHAT_MSG_RECV):
- who = self._get_jid_from_jid_id(jid_id)
- elif kind in (KindConstant.SINGLE_MSG_SENT,
- KindConstant.CHAT_MSG_SENT):
- who = _('You')
- elif kind == KindConstant.GC_MSG:
- who = nickname
- else: # status or gc_status. do not save
- #print kind
- continue
-
- try:
- time_ = time.strftime('%c', time.localtime(float(time_)))
- except ValueError:
- pass
-
- file_.write(_('%(who)s on %(time)s said: %(message)s\n') % {
- 'who': who, 'time': time_, 'message': message})
-
- def _delete_jid_logs(self, liststore, list_of_paths):
- paths_len = len(list_of_paths)
- if paths_len == 0: # nothing is selected
- return
-
- def on_ok():
- # delete all rows from db that match jid_id
- list_of_rowrefs = []
- for path in list_of_paths: # make them treerowrefs (it's needed)
- list_of_rowrefs.append(Gtk.TreeRowReference.new(
- liststore, path))
-
- for rowref in list_of_rowrefs:
- path = rowref.get_path()
- if path is None:
- continue
- jid_id = liststore[path][1]
- del liststore[path] # remove from UI
- # remove from db
- self.cur.execute('''
- DELETE FROM logs
- WHERE jid_id = ?
- ''', (jid_id,))
-
- # now delete "jid, jid_id" row from jids table
- self.cur.execute('''
- DELETE FROM jids
- WHERE jid_id = ?
- ''', (jid_id,))
-
- self.con.commit()
-
- self.AT_LEAST_ONE_DELETION_DONE = True
-
- ConfirmationDialog(
- _('Delete'),
- ngettext('Delete Conversation', 'Delete Conversations', paths_len),
- ngettext('Do you want to permanently delete this '
- 'conversation with <b>%s</b>?',
- 'Do you want to permanently delete these conversations?',
- paths_len, liststore[list_of_paths[0]][0]),
- [DialogButton.make('Cancel'),
- DialogButton.make('Delete',
- callback=on_ok)],
- transient_for=self._ui.history_manager_window).show()
-
- def _delete_logs(self, liststore, list_of_paths):
- paths_len = len(list_of_paths)
- if paths_len == 0: # nothing is selected
- return
-
- def on_ok():
- # delete rows from db that match log_line_id
- list_of_rowrefs = []
- for path in list_of_paths: # make them treerowrefs (it's needed)
- list_of_rowrefs.append(Gtk.TreeRowReference.new(
- liststore, path))
-
- for rowref in list_of_rowrefs:
- path = rowref.get_path()
- if path is None:
- continue
- log_line_id = liststore[path][0]
- del liststore[path] # remove from UI
- # remove from db
- self.cur.execute('''
- DELETE FROM logs
- WHERE log_line_id = ?
- ''', (log_line_id,))
-
- self.con.commit()
-
- self.AT_LEAST_ONE_DELETION_DONE = True
-
- ConfirmationDialog(
- _('Delete'),
- ngettext('Delete Message', 'Delete Messages', paths_len),
- ngettext('Do you want to permanently delete this message?',
- 'Do you want to permanently delete these messages?',
- paths_len),
- [DialogButton.make('Cancel'),
- DialogButton.make('Delete',
- callback=on_ok)],
- transient_for=self._ui.history_manager_window).show()
-
- def on_search_db_button_clicked(self, _widget):
- text = self._ui.search_entry.get_text()
- if not text:
- return
-
- self._ui.welcome_box.hide()
- self._ui.logs_scrolledwindow.hide()
- self._ui.search_results_scrolledwindow.show()
-
- self._fill_search_results_listview(text)
-
- def on_search_results_listview_row_activated(self, _widget, path, _column):
- # get log_line_id, jid_id from row we double clicked
- log_line_id = str(self.search_results_liststore[path][0])
- jid = self.search_results_liststore[path][1]
- # make it string as in gtk liststores I have them all as strings
- # as this is what db returns so I don't have to fight with types
- jid_id = self._get_jid_id(jid)
-
- iter_ = self.jids_liststore.get_iter_first()
- while iter_:
- # self.jids_liststore[iter_][1] holds jid_ids
- if self.jids_liststore[iter_][1] == jid_id:
- break
- iter_ = self.jids_liststore.iter_next(iter_)
-
- if iter_ is None:
- return
-
- path = self.jids_liststore.get_path(iter_)
- self._ui.jids_listview.set_cursor(path)
-
- iter_ = self.logs_liststore.get_iter_first()
- while iter_:
- # self.logs_liststore[iter_][0] holds log_line_ids
- if self.logs_liststore[iter_][0] == log_line_id:
- break
- iter_ = self.logs_liststore.iter_next(iter_)
-
- if iter_ is None:
- return
-
- path = self.logs_liststore.get_path(iter_)
- self._ui.logs_listview.scroll_to_cell(path)
- self._ui.logs_listview.get_selection().select_path(path)
-
-
-def main() -> None:
- if sys.platform != 'win32':
- if os.geteuid() == 0:
- sys.exit("You must not launch gajim as root, it is insecure.")
-
- HistoryManager()
- Gtk.main()
-
-
-if __name__ == '__main__':
- main()
diff --git a/launch-history-manager.py b/launch-history-manager.py
deleted file mode 100755
index 28c314d0f..000000000
--- a/launch-history-manager.py
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/env python3
-
-from gajim import history_manager
-history_manager.main()
diff --git a/setup.py b/setup.py
index de60c6a52..d759c0acd 100644
--- a/setup.py
+++ b/setup.py
@@ -16,7 +16,7 @@ from pathlib import Path
pos = Path('po').glob('*.po')
ALL_LINGUAS = sorted([x.stem for x in pos])
-MAN_FILES = ['gajim.1', 'gajim-history-manager.1', 'gajim-remote.1']
+MAN_FILES = ['gajim.1', 'gajim-remote.1']
META_FILES = [('data/org.gajim.Gajim.desktop', 'share/applications', '--desktop'),
('data/org.gajim.Gajim.appdata.xml', 'share/metainfo', '--xml')]
cwd = Path(__file__).resolve().parent
@@ -209,7 +209,6 @@ setup(
],
'gui_scripts': [
'gajim = gajim.gajim:main',
- 'gajim-history-manager = gajim.history_manager:main',
]
},
data_files=data_files
diff --git a/win/_base.sh b/win/_base.sh
index 4d1010ea4..9f1a4514f 100644
--- a/win/_base.sh
+++ b/win/_base.sh
@@ -191,7 +191,7 @@ function cleanup_install {
fi
done
- KEEP="gajim|gajim-debug|python3|gajim-history-manager|gdbus|gspawn-win32-helper|gspawn-win64-helper"
+ KEEP="gajim|gajim-debug|python3|gdbus|gspawn-win32-helper|gspawn-win64-helper"
echo "deleting .exe files"
find "${MINGW_ROOT}" -regextype "posix-extended" -name "*.exe" -and ! \