Add URL handler to open help files in web browser
Resolves: RHEL-76398
This commit is contained in:
parent
436d41f0ca
commit
012f30c3d0
304
url-handler.patch
Normal file
304
url-handler.patch
Normal file
@ -0,0 +1,304 @@
|
||||
From 66d2d8d8239f781f20f913dbe388b52e14f8dffb Mon Sep 17 00:00:00 2001
|
||||
From: Ray Strode <rstrode@redhat.com>
|
||||
Date: Thu, 18 May 2023 11:28:37 -0400
|
||||
Subject: [PATCH] tools: Add a url handler that generates html from
|
||||
documentation
|
||||
|
||||
Some systems don't install yelp by default but still want to show
|
||||
documentation.
|
||||
|
||||
This commit adds a small url handler to automatically convert
|
||||
documentation to html format and then launch a web browser to show
|
||||
it.
|
||||
---
|
||||
meson_options.txt | 4 +
|
||||
tools/meson.build | 24 +++
|
||||
tools/yelp-build-url-handler.desktop.in | 6 +
|
||||
tools/yelp-build-url-handler.in | 212 ++++++++++++++++++++++++
|
||||
4 files changed, 246 insertions(+)
|
||||
create mode 100644 tools/yelp-build-url-handler.desktop.in
|
||||
create mode 100755 tools/yelp-build-url-handler.in
|
||||
|
||||
diff --git a/meson_options.txt b/meson_options.txt
|
||||
index 0526e6f..03e9050 100644
|
||||
--- a/meson_options.txt
|
||||
+++ b/meson_options.txt
|
||||
@@ -5,3 +5,7 @@ option('yelpm4',
|
||||
option('help',
|
||||
type: 'boolean', value: false,
|
||||
description: 'Install help files')
|
||||
+
|
||||
+option('url_handler',
|
||||
+ type: 'feature', value: 'disabled',
|
||||
+ description: 'Install URL handler to open help in web browesr, use when not building Yelp')
|
||||
diff --git a/tools/meson.build b/tools/meson.build
|
||||
index 35187ca..b2518d6 100644
|
||||
--- a/tools/meson.build
|
||||
+++ b/tools/meson.build
|
||||
@@ -1,5 +1,10 @@
|
||||
yelp_tools_in = configuration_data()
|
||||
yelp_tools_in.set('DATADIR', pkgdir)
|
||||
+yelp_tools_in.set('HELPDIR', join_paths (
|
||||
+ datadir,
|
||||
+ 'help',
|
||||
+ )
|
||||
+)
|
||||
|
||||
yelp_tools_in.set('YELP_JS_DIR', yelp_js_dir)
|
||||
|
||||
@@ -48,3 +53,22 @@ if get_option('yelpm4') == true
|
||||
)
|
||||
)
|
||||
endif
|
||||
+
|
||||
+if get_option('url_handler').enabled()
|
||||
+ configure_file(
|
||||
+ input: 'yelp-build-url-handler.in',
|
||||
+ output: 'yelp-build-url-handler',
|
||||
+ configuration: yelp_tools_in,
|
||||
+ install: true,
|
||||
+ install_dir: bindir,
|
||||
+ )
|
||||
+
|
||||
+ configure_file(
|
||||
+ input: 'yelp-build-url-handler.desktop.in',
|
||||
+ output: '@BASENAME@',
|
||||
+ configuration: {
|
||||
+ 'bindir': join_paths(get_option('prefix'), get_option('bindir'))
|
||||
+ },
|
||||
+ install_dir: join_paths(datadir, 'applications')
|
||||
+ )
|
||||
+endif
|
||||
diff --git a/tools/yelp-build-url-handler.desktop.in b/tools/yelp-build-url-handler.desktop.in
|
||||
new file mode 100644
|
||||
index 0000000..9ead646
|
||||
--- /dev/null
|
||||
+++ b/tools/yelp-build-url-handler.desktop.in
|
||||
@@ -0,0 +1,6 @@
|
||||
+[Desktop Entry]
|
||||
+Name=Yelp URL Handler
|
||||
+Exec=@bindir@/yelp-build-url-handler %u
|
||||
+Type=Application
|
||||
+MimeType=x-scheme-handler/help;
|
||||
+NoDisplay=true
|
||||
diff --git a/tools/yelp-build-url-handler.in b/tools/yelp-build-url-handler.in
|
||||
new file mode 100755
|
||||
index 0000000..8742d67
|
||||
--- /dev/null
|
||||
+++ b/tools/yelp-build-url-handler.in
|
||||
@@ -0,0 +1,212 @@
|
||||
+#!/usr/bin/python3
|
||||
+#
|
||||
+# yelp-build-url-handler
|
||||
+# Copyright (C) 2023, 2024 Red Hat Inc.
|
||||
+#
|
||||
+# This program 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; either version 2 of the License, or
|
||||
+# (at your option) any later version.
|
||||
+#
|
||||
+# This program 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 this program; if not, write to the Free Software
|
||||
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
+
|
||||
+import os
|
||||
+import sys
|
||||
+import subprocess
|
||||
+import urllib.parse
|
||||
+import gi
|
||||
+import errno
|
||||
+import dbus
|
||||
+from dbus import Interface, SessionBus
|
||||
+
|
||||
+gi.require_version('Gio', '2.0')
|
||||
+from gi.repository import Gio, GLib
|
||||
+
|
||||
+class YelpBuildUrlHandler:
|
||||
+ def main(self):
|
||||
+ if len(sys.argv) != 2:
|
||||
+ print('Usage: yelp-build-url-handler <URL>', file=sys.stderr)
|
||||
+ return 1
|
||||
+ url = sys.argv[1]
|
||||
+ return self.handle_url(url)
|
||||
+
|
||||
+ def determine_paths_from_url(self, url):
|
||||
+ parsed_url = urllib.parse.urlparse(url)
|
||||
+ if parsed_url.scheme != 'help':
|
||||
+ print(f'Invalid URI scheme: {parsed_url.scheme}', file=sys.stderr)
|
||||
+ return None
|
||||
+
|
||||
+ path = parsed_url.path.lstrip('/')
|
||||
+ languages = GLib.get_language_names() + ['C']
|
||||
+
|
||||
+ for language in languages:
|
||||
+ base_path = os.path.join('@HELPDIR@', language)
|
||||
+ cache_path = os.path.join(GLib.get_user_cache_dir(), 'yelp-build', language)
|
||||
+ full_source_path = os.path.join(base_path, path)
|
||||
+
|
||||
+ if os.path.isdir(full_source_path):
|
||||
+ source_dir = full_source_path
|
||||
+ output_dir = os.path.join(cache_path, path)
|
||||
+ output_file = os.path.join(output_dir, 'index.html')
|
||||
+ return {
|
||||
+ 'source_dir': source_dir,
|
||||
+ 'output_dir': output_dir,
|
||||
+ 'output_file': output_file,
|
||||
+ 'full_source_path': full_source_path
|
||||
+ }
|
||||
+ else:
|
||||
+ full_source_file = full_source_path + '.page'
|
||||
+ if os.path.isfile(full_source_file):
|
||||
+ source_dir = os.path.dirname(full_source_file)
|
||||
+ output_file = os.path.join(cache_path, path + '.html')
|
||||
+ output_dir = os.path.dirname(output_file)
|
||||
+ return {
|
||||
+ 'source_dir': source_dir,
|
||||
+ 'output_dir': output_dir,
|
||||
+ 'output_file': output_file,
|
||||
+ 'full_source_path': full_source_file
|
||||
+ }
|
||||
+
|
||||
+ print(f'Help content not found for path: {path}', file=sys.stderr)
|
||||
+ return None
|
||||
+
|
||||
+ def handle_url(self, url):
|
||||
+ paths = self.determine_paths_from_url(url)
|
||||
+
|
||||
+ if paths is None:
|
||||
+ return 1
|
||||
+
|
||||
+ source_dir = paths['source_dir']
|
||||
+ output_dir = paths['output_dir']
|
||||
+ output_file = paths['output_file']
|
||||
+ full_source_path = paths['full_source_path']
|
||||
+
|
||||
+ cache_stale = True
|
||||
+ try:
|
||||
+ output_status = os.stat(output_dir)
|
||||
+ source_status = os.stat(source_dir)
|
||||
+ if output_status.st_mtime < source_status.st_mtime:
|
||||
+ cache_stale = False
|
||||
+ except OSError as e:
|
||||
+ pass
|
||||
+
|
||||
+ if cache_stale:
|
||||
+ cache_stale = not self.generate_html(source_dir, output_dir)
|
||||
+
|
||||
+ if cache_stale:
|
||||
+ print(f'Failed to generate HTML for path {full_source_path}', file=sys.stderr)
|
||||
+ return 1
|
||||
+
|
||||
+ sandboxed_dir = self.grant_access_to_browser(output_dir)
|
||||
+ if sandboxed_dir is None:
|
||||
+ print(f'Failed to grant browser access to generated HTML for path {full_source_path}', file=sys.stderr)
|
||||
+ return 1
|
||||
+
|
||||
+ basename = os.path.basename(output_file)
|
||||
+ sandboxed_file = os.path.join(sandboxed_dir, basename)
|
||||
+ redirect_file = self.write_redirect_file(sandboxed_dir, sandboxed_file)
|
||||
+ if redirect_file is None:
|
||||
+ return 1
|
||||
+
|
||||
+ if not self.show_generated_html(redirect_file):
|
||||
+ print('Failed to start browser', file=sys.stderr)
|
||||
+ return 1
|
||||
+
|
||||
+ def generate_html(self, source_dir, output_dir):
|
||||
+ os.makedirs(output_dir, exist_ok=True)
|
||||
+ argv = ['yelp-build', 'html', '.', '-o', output_dir]
|
||||
+ try:
|
||||
+ subprocess.run(argv, cwd=source_dir, check=True)
|
||||
+ return True
|
||||
+ except subprocess.CalledProcessError as e:
|
||||
+ print(f'Could not run "{" ".join(argv)}": {e}', file=sys.stderr)
|
||||
+ return False
|
||||
+
|
||||
+ def grant_access_to_browser(self, output_dir):
|
||||
+ default_browser = Gio.AppInfo.get_default_for_type('text/html', False)
|
||||
+ if default_browser is None:
|
||||
+ print('Default web browser not found!', file=sys.stderr)
|
||||
+ return None
|
||||
+
|
||||
+ app_id = default_browser.get_id()
|
||||
+ if app_id is None:
|
||||
+ print('Default web browser has no app ID!', file=sys.stderr)
|
||||
+ return None
|
||||
+
|
||||
+ app_id = app_id.removesuffix('.desktop')
|
||||
+
|
||||
+ try:
|
||||
+ dir_fd = os.open(output_dir, os.O_PATH)
|
||||
+ except OSError as e:
|
||||
+ print(f'Failed to open directory {output_dir}: {e}', file=sys.stderr)
|
||||
+ return None
|
||||
+
|
||||
+ bus = SessionBus()
|
||||
+ try:
|
||||
+ portal_documents_object = bus.get_object('org.freedesktop.portal.Documents', '/org/freedesktop/portal/documents')
|
||||
+ portal_documents = Interface(portal_documents_object, dbus_interface='org.freedesktop.portal.Documents')
|
||||
+ except dbus.DBusException as e:
|
||||
+ print(f'Failed to connect to Documents portal: {e}', file=sys.stderr)
|
||||
+ os.close(dir_fd)
|
||||
+ return None
|
||||
+
|
||||
+ flags = (
|
||||
+ 1 << 0 # ReuseExisting
|
||||
+ | 1 << 1 # Persistent
|
||||
+ | 1 << 3 # ExportDirectory
|
||||
+ )
|
||||
+ permissions = ['read']
|
||||
+
|
||||
+ try:
|
||||
+ doc_with_signature, *_ = portal_documents.AddFull([dbus.types.UnixFd(dir_fd)], flags, app_id, permissions)
|
||||
+ if not doc_with_signature:
|
||||
+ print('No document IDs returned from portal', file=sys.stderr)
|
||||
+ os.close(dir_fd)
|
||||
+ return None
|
||||
+
|
||||
+ doc_id, *_ = doc_with_signature
|
||||
+ sandboxed_dir = os.path.join(GLib.get_user_runtime_dir(), 'doc', doc_id, os.path.basename(output_dir))
|
||||
+ os.close(dir_fd)
|
||||
+ except dbus.DBusException as e:
|
||||
+ print(f'Failed to call AddFull method: {e}', file=sys.stderr)
|
||||
+ os.close(dir_fd)
|
||||
+ return None
|
||||
+
|
||||
+ return sandboxed_dir
|
||||
+
|
||||
+ def show_generated_html(self, output):
|
||||
+ try:
|
||||
+ uri = GLib.filename_to_uri(output)
|
||||
+ Gio.AppInfo.launch_default_for_uri(uri, None)
|
||||
+ except Exception as e:
|
||||
+ print(f'Failed to launch default application: {e}', file=sys.stderr)
|
||||
+ return False
|
||||
+
|
||||
+ return True
|
||||
+
|
||||
+ def write_redirect_file(self, sandboxed_dir, sandboxed_file):
|
||||
+ html = f'<meta http-equiv="refresh" content="0; url=file://{sandboxed_file}">'
|
||||
+ redirect_file = os.path.join(sandboxed_dir, 'yelp-build-redirect.html')
|
||||
+
|
||||
+ try:
|
||||
+ with open(redirect_file, 'w') as f:
|
||||
+ f.write(html)
|
||||
+ except Exception as e:
|
||||
+ print(f'Error writing redirect file {redirect_file}: {e}', file=sys.stderr)
|
||||
+ return None
|
||||
+
|
||||
+ return redirect_file
|
||||
+
|
||||
+if __name__ == '__main__':
|
||||
+ try:
|
||||
+ sys.exit(YelpBuildUrlHandler().main())
|
||||
+ except KeyboardInterrupt:
|
||||
+ sys.exit(1)
|
||||
+
|
||||
--
|
||||
GitLab
|
||||
|
@ -1,8 +1,10 @@
|
||||
%global tarball_version %%(echo %{version} | tr '~' '.')
|
||||
|
||||
%bcond url_handler 0%{?rhel}
|
||||
|
||||
Name: yelp-tools
|
||||
Version: 42.1
|
||||
Release: 7%{?dist}
|
||||
Release: 8%{?dist}
|
||||
Summary: Create, manage, and publish documentation for Yelp
|
||||
|
||||
License: GPL-2.0-or-later
|
||||
@ -10,6 +12,9 @@ URL: https://wiki.gnome.org/Apps/Yelp/Tools
|
||||
Source0: https://download.gnome.org/sources/%{name}/42/%{name}-%{tarball_version}.tar.xz
|
||||
BuildArch: noarch
|
||||
|
||||
# https://gitlab.gnome.org/GNOME/yelp-tools/-/merge_requests/12
|
||||
Patch: url-handler.patch
|
||||
|
||||
BuildRequires: meson
|
||||
BuildRequires: pkgconfig(yelp-xsl)
|
||||
BuildRequires: python3-lxml
|
||||
@ -33,7 +38,11 @@ wraps things up in a developer-friendly way.
|
||||
%autosetup -p1 -n %{name}-%{tarball_version}
|
||||
|
||||
%build
|
||||
%meson
|
||||
%meson \
|
||||
%if %{with url_handler}
|
||||
-Durl_handler=enabled \
|
||||
%endif
|
||||
%{nil}
|
||||
%meson_build
|
||||
|
||||
%install
|
||||
@ -43,12 +52,22 @@ wraps things up in a developer-friendly way.
|
||||
%doc AUTHORS README.md NEWS
|
||||
%license COPYING COPYING.GPL
|
||||
%{_bindir}/yelp-build
|
||||
%if %{with url_handler}
|
||||
%{_bindir}/yelp-build-url-handler
|
||||
%endif
|
||||
%{_bindir}/yelp-check
|
||||
%{_bindir}/yelp-new
|
||||
%if %{with url_handler}
|
||||
%{_datadir}/applications/yelp-build-url-handler.desktop
|
||||
%endif
|
||||
%{_datadir}/yelp-tools
|
||||
%{_datadir}/aclocal/yelp.m4
|
||||
|
||||
%changelog
|
||||
* Mon Jan 27 2025 Michael Catanzaro <mcatanzaro@redhat.com> - 42.1-8
|
||||
- Add URL handler to open help files in web browser
|
||||
Resolves: RHEL-76398
|
||||
|
||||
* Tue Oct 29 2024 Troy Dawson <tdawson@redhat.com> - 42.1-7
|
||||
- Bump release for October 2024 mass rebuild:
|
||||
Resolves: RHEL-64018
|
||||
|
Loading…
Reference in New Issue
Block a user