Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Vladislav Yarmak
gajim-plugins
Commits
37169017
Commit
37169017
authored
Oct 28, 2017
by
Thilo Molitor
Browse files
[preview] Port to Gtk3
parent
a4b62296
Changes
6
Expand all
Hide whitespace changes
Inline
Side-by-side
url_image_preview/__init__.py
View file @
37169017
# simple redirect
from
.url_image_preview
import
UrlImagePreviewPlugin
url_image_preview/config_dialog.ui
View file @
37169017
<?xml version="1.0"?>
<?xml version="1.0"
encoding="UTF-8"
?>
<interface>
<requires
lib=
"gtk+"
version=
"2.16"
/>
<!-- interface-naming-policy toplevel-contextual -->
<requires
lib=
"gtk+"
version=
"3.0"
/>
<object
class=
"GtkListStore"
id=
"liststore1"
>
<columns>
<!-- column-name Text -->
<column
type=
"gchararray"
/>
</columns>
<data>
<row>
<col
id=
"0"
translatable=
"yes"
>
256 KiB
</col>
</row>
<row>
<col
id=
"0"
translatable=
"yes"
>
512 KiB
</col>
</row>
<row>
<col
id=
"0"
translatable=
"yes"
>
1 MiB
</col>
</row>
<row>
<col
id=
"0"
translatable=
"yes"
>
5 MiB
</col>
</row>
<row>
<col
id=
"0"
translatable=
"yes"
>
10 MiB
</col>
</row>
</data>
</object>
<object
class=
"GtkListStore"
id=
"liststore2"
>
<columns>
<!-- column-name Text -->
<column
type=
"gchararray"
/>
</columns>
<data>
<row>
<col
id=
"0"
translatable=
"yes"
>
Open
</col>
</row>
<row>
<col
id=
"0"
translatable=
"yes"
>
Save as
</col>
</row>
<row>
<col
id=
"0"
translatable=
"yes"
>
Copy Link Location
</col>
</row>
<row>
<col
id=
"0"
translatable=
"yes"
>
Open Link in Browser
</col>
</row>
<row>
<col
id=
"0"
translatable=
"yes"
>
Open Downloaded File in Browser
</col>
</row>
</data>
</object>
<object
class=
"GtkWindow"
id=
"window1"
>
<property
name=
"can_focus"
>
False
</property>
<child>
<object
class=
"GtkVBox"
id=
"vbox1"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"orientation"
>
vertical
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"border_width"
>
9
</property>
<child>
<object
class=
"Gtk
HBox
"
id=
"
hbox2
"
>
<object
class=
"Gtk
Frame
"
id=
"
frame1
"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"label_xalign"
>
0
</property>
<property
name=
"shadow_type"
>
none
</property>
<child>
<object
class=
"GtkLabel"
id=
"preview_size_lebel"
>
<property
name=
"width_request"
>
133
</property>
<property
name=
"visible"
>
True
</property>
<property
name=
"xalign"
>
0.029999999329447746
</property>
<property
name=
"label"
translatable=
"yes"
>
Preview size
</property>
<property
name=
"ellipsize"
>
start
</property>
<property
name=
"single_line_mode"
>
True
</property>
<property
name=
"track_visited_links"
>
False
</property>
</object>
<packing>
<property
name=
"position"
>
0
</property>
</packing>
</child>
<child>
<object
class=
"GtkSpinButton"
id=
"preview_size"
>
<object
class=
"GtkTable"
id=
"table1"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
True
</property>
<property
name=
"has_tooltip"
>
True
</property>
<property
name=
"tooltip_text"
translatable=
"yes"
>
Preview size(10-512)
</property>
<property
name=
"invisible_char"
>
●
</property>
<property
name=
"width_chars"
>
6
</property>
<property
name=
"snap_to_ticks"
>
True
</property>
<property
name=
"numeric"
>
True
</property>
<signal
name=
"value_changed"
handler=
"preview_size_value_changed"
/>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"n_rows"
>
3
</property>
<property
name=
"n_columns"
>
2
</property>
<child>
<object
class=
"GtkSpinButton"
id=
"preview_size"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
True
</property>
<property
name=
"invisible_char"
>
●
</property>
<property
name=
"width_chars"
>
6
</property>
<property
name=
"primary_icon_activatable"
>
False
</property>
<property
name=
"secondary_icon_activatable"
>
False
</property>
<property
name=
"primary_icon_sensitive"
>
True
</property>
<property
name=
"secondary_icon_sensitive"
>
True
</property>
<property
name=
"snap_to_ticks"
>
True
</property>
<property
name=
"numeric"
>
True
</property>
<signal
name=
"value-changed"
handler=
"preview_size_value_changed"
swapped=
"no"
/>
</object>
<packing>
<property
name=
"left_attach"
>
1
</property>
<property
name=
"right_attach"
>
2
</property>
<property
name=
"y_options"
/>
</packing>
</child>
<child>
<object
class=
"GtkComboBox"
id=
"max_size_combobox"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"model"
>
liststore1
</property>
<signal
name=
"changed"
handler=
"max_size_value_changed"
swapped=
"no"
/>
<child>
<object
class=
"GtkCellRendererText"
id=
"cellrenderertext1"
/>
<attributes>
<attribute
name=
"text"
>
0
</attribute>
</attributes>
</child>
</object>
<packing>
<property
name=
"left_attach"
>
1
</property>
<property
name=
"right_attach"
>
2
</property>
<property
name=
"top_attach"
>
1
</property>
<property
name=
"bottom_attach"
>
2
</property>
<property
name=
"y_options"
>
GTK_EXPAND
</property>
</packing>
</child>
<child>
<object
class=
"GtkComboBox"
id=
"leftclick_action_combobox"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"model"
>
liststore2
</property>
<signal
name=
"changed"
handler=
"leftclick_action_changed"
swapped=
"no"
/>
<child>
<object
class=
"GtkCellRendererText"
id=
"cellrenderertext2"
/>
<attributes>
<attribute
name=
"text"
>
0
</attribute>
</attributes>
</child>
</object>
<packing>
<property
name=
"left_attach"
>
1
</property>
<property
name=
"right_attach"
>
2
</property>
<property
name=
"top_attach"
>
2
</property>
<property
name=
"bottom_attach"
>
3
</property>
<property
name=
"y_options"
>
GTK_EXPAND
</property>
</packing>
</child>
<child>
<object
class=
"GtkLabel"
id=
"max_size_label"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"xalign"
>
0
</property>
<property
name=
"xpad"
>
13
</property>
<property
name=
"label"
translatable=
"yes"
>
Accept files smaller then
</property>
<property
name=
"track_visited_links"
>
False
</property>
</object>
<packing>
<property
name=
"top_attach"
>
1
</property>
<property
name=
"bottom_attach"
>
2
</property>
<property
name=
"y_options"
>
GTK_EXPAND
</property>
</packing>
</child>
<child>
<object
class=
"GtkLabel"
id=
"preview_size_label"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"xalign"
>
0
</property>
<property
name=
"xpad"
>
12
</property>
<property
name=
"label"
translatable=
"yes"
>
Preview size
</property>
<property
name=
"track_visited_links"
>
False
</property>
</object>
<packing>
<property
name=
"y_options"
>
GTK_EXPAND
</property>
</packing>
</child>
<child>
<object
class=
"GtkLabel"
id=
"leftclick_action_label"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"xalign"
>
0
</property>
<property
name=
"xpad"
>
12
</property>
<property
name=
"label"
translatable=
"yes"
>
Left click action
</property>
<property
name=
"track_visited_links"
>
False
</property>
</object>
<packing>
<property
name=
"y_options"
>
GTK_EXPAND
</property>
<property
name=
"top_attach"
>
2
</property>
<property
name=
"bottom_attach"
>
3
</property>
<property
name=
"y_options"
>
GTK_EXPAND
</property>
</packing>
</child>
</object>
<packing>
<property
name=
"expand"
>
False
</property>
<property
name=
"fill"
>
False
</property>
<property
name=
"position"
>
1
</property>
</packing>
</child>
</object>
<packing>
<property
name=
"expand"
>
True
</property>
<property
name=
"fill"
>
True
</property>
<property
name=
"padding"
>
6
</property>
<property
name=
"position"
>
0
</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
...
...
url_image_preview/context_menu.ui
0 → 100644
View file @
37169017
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires
lib=
"gtk+"
version=
"3.0"
/>
<object
class=
"GtkImage"
id=
"image1"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"stock"
>
gtk-open
</property>
</object>
<object
class=
"GtkImage"
id=
"image2"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"stock"
>
gtk-save-as
</property>
</object>
<object
class=
"GtkImage"
id=
"image3"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"stock"
>
gtk-copy
</property>
</object>
<object
class=
"GtkImage"
id=
"image4"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"stock"
>
gtk-jump-to
</property>
</object>
<object
class=
"GtkImage"
id=
"image5"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"stock"
>
gtk-jump-to
</property>
</object>
<object
class=
"GtkMenu"
id=
"context_menu"
>
<property
name=
"can_focus"
>
False
</property>
<child>
<object
class=
"GtkImageMenuItem"
id=
"open_menuitem"
>
<property
name=
"label"
translatable=
"yes"
>
_Open
</property>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"use_underline"
>
True
</property>
<property
name=
"image"
>
image1
</property>
<property
name=
"use_stock"
>
False
</property>
<property
name=
"always_show_image"
>
True
</property>
</object>
</child>
<child>
<object
class=
"GtkImageMenuItem"
id=
"save_as_menuitem"
>
<property
name=
"label"
translatable=
"yes"
>
_Save as
</property>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"use_underline"
>
True
</property>
<property
name=
"image"
>
image2
</property>
<property
name=
"use_stock"
>
False
</property>
<property
name=
"always_show_image"
>
True
</property>
</object>
</child>
<child>
<object
class=
"GtkSeparatorMenuItem"
id=
"encryption_separator"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
</object>
</child>
<child>
<object
class=
"GtkImageMenuItem"
id=
"copy_link_location_menuitem"
>
<property
name=
"label"
translatable=
"yes"
>
_Copy Link Location
</property>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"use_underline"
>
True
</property>
<property
name=
"image"
>
image3
</property>
<property
name=
"use_stock"
>
False
</property>
<property
name=
"always_show_image"
>
True
</property>
</object>
</child>
<child>
<object
class=
"GtkImageMenuItem"
id=
"open_link_in_browser_menuitem"
>
<property
name=
"label"
translatable=
"yes"
>
Open Link in _Browser
</property>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"use_underline"
>
True
</property>
<property
name=
"image"
>
image4
</property>
<property
name=
"use_stock"
>
False
</property>
<property
name=
"always_show_image"
>
True
</property>
</object>
</child>
<child>
<object
class=
"GtkSeparatorMenuItem"
id=
"extras_separator"
>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
</object>
</child>
<child>
<object
class=
"GtkImageMenuItem"
id=
"open_file_in_browser_menuitem"
>
<property
name=
"label"
translatable=
"yes"
>
Open _Downloaded File in Browser
</property>
<property
name=
"visible"
>
True
</property>
<property
name=
"can_focus"
>
False
</property>
<property
name=
"use_underline"
>
True
</property>
<property
name=
"image"
>
image5
</property>
<property
name=
"use_stock"
>
False
</property>
<property
name=
"always_show_image"
>
True
</property>
</object>
</child>
</object>
</interface>
url_image_preview/http_functions.py
0 → 100644
View file @
37169017
# -*- coding: utf-8 -*-
##
## 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/>.
##
import
urllib.request
as
urllib2
import
socket
import
re
from
gajim.common
import
app
from
gajim.common
import
helpers
import
logging
import
os
if
os
.
name
==
'nt'
:
import
certifi
if
app
.
HAVE_PYCURL
:
import
pycurl
from
io
import
StringIO
log
=
logging
.
getLogger
(
'gajim.plugin_system.url_image_preview.http_functions'
)
def
get_http_head
(
account
,
url
):
# Check if proxy is used
proxy
=
helpers
.
get_proxy_info
(
account
)
if
proxy
and
proxy
[
'type'
]
in
(
'http'
,
'socks5'
):
return
_get_http_head_proxy
(
url
,
proxy
)
return
_get_http_head_direct
(
url
)
def
get_http_file
(
account
,
attrs
):
# Check if proxy is used
proxy
=
helpers
.
get_proxy_info
(
account
)
if
proxy
and
proxy
[
'type'
]
in
(
'http'
,
'socks5'
):
return
_get_http_proxy
(
attrs
,
proxy
)
else
:
return
_get_http_direct
(
attrs
)
def
_get_http_head_direct
(
url
):
log
.
debug
(
'Head request direct for URL: %s'
%
url
)
try
:
req
=
urllib2
.
Request
(
url
)
req
.
get_method
=
lambda
:
'HEAD'
req
.
add_header
(
'User-Agent'
,
'Gajim %s'
%
app
.
version
)
if
os
.
name
==
'nt'
:
f
=
urllib2
.
urlopen
(
req
,
cafile
=
certifi
.
where
())
else
:
f
=
urllib2
.
urlopen
(
req
)
except
Exception
as
ex
:
log
.
debug
(
'Could not get head response for URL: %s'
%
url
)
log
.
debug
(
"%s"
%
str
(
ex
))
return
(
''
,
0
)
ctype
=
f
.
headers
[
'Content-Type'
]
clen
=
f
.
headers
[
'Content-Length'
]
try
:
clen
=
int
(
clen
)
except
ValueError
:
pass
return
(
ctype
,
clen
)
def
_get_http_head_proxy
(
url
,
proxy
):
log
.
debug
(
'Head request with proxy for URL: %s'
%
url
)
if
not
app
.
HAVE_PYCURL
:
log
.
error
(
'PYCURL not installed'
)
return
(
''
,
0
)
headers
=
''
try
:
b
=
StringIO
()
c
=
pycurl
.
Curl
()
c
.
setopt
(
pycurl
.
URL
,
url
.
encode
(
'utf-8'
))
c
.
setopt
(
pycurl
.
FOLLOWLOCATION
,
1
)
# Make a HEAD request:
c
.
setopt
(
pycurl
.
CUSTOMREQUEST
,
'HEAD'
)
c
.
setopt
(
pycurl
.
NOBODY
,
1
)
c
.
setopt
(
pycurl
.
HEADER
,
1
)
c
.
setopt
(
pycurl
.
MAXFILESIZE
,
2000000
)
c
.
setopt
(
pycurl
.
WRITEFUNCTION
,
b
.
write
)
c
.
setopt
(
pycurl
.
USERAGENT
,
'Gajim '
+
app
.
version
)
# set proxy
c
.
setopt
(
pycurl
.
PROXY
,
proxy
[
'host'
].
encode
(
'utf-8'
))
c
.
setopt
(
pycurl
.
PROXYPORT
,
proxy
[
'port'
])
if
proxy
[
'useauth'
]:
c
.
setopt
(
pycurl
.
PROXYUSERPWD
,
proxy
[
'user'
].
encode
(
'utf-8'
)
+
':'
+
proxy
[
'pass'
].
encode
(
'utf-8'
))
c
.
setopt
(
pycurl
.
PROXYAUTH
,
pycurl
.
HTTPAUTH_ANY
)
if
proxy
[
'type'
]
==
'http'
:
c
.
setopt
(
pycurl
.
PROXYTYPE
,
pycurl
.
PROXYTYPE_HTTP
)
elif
proxy
[
'type'
]
==
'socks5'
:
c
.
setopt
(
pycurl
.
PROXYTYPE
,
pycurl
.
PROXYTYPE_SOCKS5
)
x
=
c
.
perform
()
c
.
close
()
headers
=
b
.
getvalue
()
except
pycurl
.
error
as
ex
:
log
.
debug
(
'Could not get head response for URL: %s'
%
url
)
log
.
debug
(
"%s"
%
str
(
ex
))
return
(
''
,
0
)
ctype
=
''
searchObj
=
re
.
search
(
r
'^Content-Type: (.*)$'
,
headers
,
re
.
M
|
re
.
I
)
if
searchObj
:
ctype
=
searchObj
.
group
(
1
).
strip
()
clen
=
0
searchObj
=
re
.
search
(
r
'^Content-Length: (.*)$'
,
headers
,
re
.
M
|
re
.
I
)
if
searchObj
:
try
:
clen
=
int
(
searchObj
.
group
(
1
).
strip
())
except
ValueError
:
pass
return
(
ctype
,
clen
)
def
_get_http_direct
(
attrs
):
"""
Download a file. This function should
be launched in a separated thread.
"""
log
.
debug
(
'Get request direct for URL: %s'
%
attrs
[
'src'
])
mem
,
alt
,
max_size
=
b
''
,
''
,
2
*
1024
*
1024
if
'max_size'
in
attrs
:
max_size
=
attrs
[
'max_size'
]
try
:
req
=
urllib2
.
Request
(
attrs
[
'src'
])
req
.
add_header
(
'User-Agent'
,
'Gajim '
+
app
.
version
)
if
os
.
name
==
'nt'
:
f
=
urllib2
.
urlopen
(
req
,
cafile
=
certifi
.
where
())
else
:
f
=
urllib2
.
urlopen
(
req
)
except
Exception
as
ex
:
log
.
debug
(
'Error loading file %s '
%
attrs
[
'src'
]
+
str
(
ex
))
pixbuf
=
None
alt
=
attrs
.
get
(
'alt'
,
'Broken image'
)
else
:
while
True
:
try
:
temp
=
f
.
read
(
100
)
except
socket
.
timeout
as
ex
:
log
.
debug
(
'Timeout loading image %s '
%
attrs
[
'src'
]
+
str
(
ex
))
alt
=
attrs
.
get
(
'alt'
,
''
)
if
alt
:
alt
+=
'
\n
'
alt
+=
_
(
'Timeout loading image'
)
break
if
temp
:
mem
+=
temp
else
:
break
if
len
(
mem
)
>
max_size
:
alt
=
attrs
.
get
(
'alt'
,
''
)
if
alt
:
alt
+=
'
\n
'
alt
+=
_
(
'Image is too big'
)
break
return
(
mem
,
alt
)
def
_get_http_proxy
(
attrs
,
proxy
):
"""
Download an image through a proxy.
This function should be launched in a
separated thread.
"""
log
.
debug
(
'Get request with proxy for URL: %s'
%
attrs
[
'src'
])
if
not
app
.
HAVE_PYCURL
:
log
.
error
(
'PYCURL not installed'
)
return
''
,
_
(
'PyCURL is not installed'
)
mem
,
alt
,
max_size
=
''
,
''
,
2
*
1024
*
1024
if
'max_size'
in
attrs
:
max_size
=
attrs
[
'max_size'
]
try
:
b
=
StringIO
()
c
=
pycurl
.
Curl
()
c
.
setopt
(
pycurl
.
URL
,
attrs
[
'src'
].
encode
(
'utf-8'
))
c
.
setopt
(
pycurl
.
FOLLOWLOCATION
,
1
)
c
.
setopt
(
pycurl
.
MAXFILESIZE
,
max_size
)
c
.
setopt
(
pycurl
.
WRITEFUNCTION
,
b
.
write
)
c
.
setopt
(
pycurl
.
USERAGENT
,
'Gajim '
+
app
.
version
)
# set proxy
c
.
setopt
(
pycurl
.
PROXY
,
proxy
[
'host'
].
encode
(
'utf-8'
))
c
.
setopt
(
pycurl
.
PROXYPORT
,
proxy
[
'port'
])
if
proxy
[
'useauth'
]:
c
.
setopt
(
pycurl
.
PROXYUSERPWD
,
proxy
[
'user'
].
encode
(
'utf-8'
)
+
':'
+
proxy
[
'pass'
].
encode
(
'utf-8'
))
c
.
setopt
(
pycurl
.
PROXYAUTH
,
pycurl
.
HTTPAUTH_ANY
)
if
proxy
[
'type'
]
==
'http'
:
c
.
setopt
(
pycurl
.
PROXYTYPE
,
pycurl
.
PROXYTYPE_HTTP
)
elif
proxy
[
'type'
]
==
'socks5'
:
c
.
setopt
(
pycurl
.
PROXYTYPE
,
pycurl
.
PROXYTYPE_SOCKS5
)
x
=
c
.
perform
()
c
.
close
()
t
=
b
.
getvalue
()
return
(
t
,
attrs
.
get
(
'alt'
,
''
))
except
pycurl
.
error
as
ex
:
alt
=
attrs
.
get
(
'alt'
,
''
)
if
alt
:
alt
+=
'
\n
'
if
ex
[
0
]
==
pycurl
.
E_FILESIZE_EXCEEDED
:
alt
+=
_
(
'Image is too big'
)
elif
ex
[
0
]
==
pycurl
.
E_OPERATION_TIMEOUTED
:
alt
+=
_
(
'Timeout loading image'
)
else
:
alt
+=
_
(
'Error loading image'
)
except
Exception
as
ex
:
log
.
debug
(
'Error loading file %s '
%
attrs
[
'src'
]
+
str
(
ex
))
pixbuf
=
None
alt
=
attrs
.
get
(
'alt'
,
'Broken image'
)
return
(
''
,
alt
)
url_image_preview/manifest.ini
View file @
37169017
...
...
@@ -2,8 +2,11 @@
name:
Url
image
preview
short_name:
url_image_preview
version:
0.5.6
description:
Url
image
preview
in
chatbox.
description:
Displays
a
preview
of
links
to
images
authors
=
Denis Fomin <fominde@gmail.com>
Yann
Leboulanger
<asterix@lagaule.org>
homepage
=
http://trac-plugins.gajim.org/wiki/UrlImagePreviewPlugin
Anders
Sandblad
<runeson@gmail.com>
Thilo
Molitor
<thilo@eightysoft.de>
Philipp
Hoerist
<philipp@hoerist.com>
homepage
=
https://dev.gajim.org/gajim/gajim-plugins/wikis/UrlImagePreviewPlugin
min_gajim_version:
0.16.11
url_image_preview/url_image_preview.py
View file @
37169017
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment