Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
PIM
KDE PIM Add-ons
Commits
a018cfa4
Commit
a018cfa4
authored
Feb 01, 2022
by
Laurent Montel
😁
Browse files
Prepare to using ktexttemplate
parent
eb38f9c1
Pipeline
#131483
passed with stage
in 9 minutes and 57 seconds
Changes
9
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
CMakeLists.txt
View file @
a018cfa4
...
...
@@ -128,9 +128,13 @@ find_package(KPimPkPass ${KPIMPKPASS_LIB_VERSION} CONFIG REQUIRED)
find_package
(
KPimItinerary
${
KPIMITINERARY_LIB_VERSION
}
CONFIG REQUIRED
)
find_package
(
KF5KontactInterface
${
KONTACTINTERFACE_LIB_VERSION
}
CONFIG REQUIRED
)
set
(
GRANTLEE_LIB_VERSION
"5.2.0"
)
find_package
(
Grantlee5
${
GRANTLEE_LIB_VERSION
}
CONFIG REQUIRED
)
if
(
QT_MAJOR_VERSION STREQUAL
"5"
)
find_package
(
Grantlee5
"5.2"
CONFIG REQUIRED
)
set
(
TEMPLATES_LIB Grantlee5::Templates
)
else
()
find_package
(
KF6TextTemplate
${
KF5_MIN_VERSION
}
CONFIG REQUIRED
)
set
(
TEMPLATES_LIB KF6TextTemplate::Templates
)
endif
()
if
(
BUILD_TESTING
)
add_definitions
(
-DBUILD_TESTING
)
...
...
plugins/messageviewer/bodypartformatter/gnupgwks/gnupgwksformatter.cpp
View file @
a018cfa4
...
...
@@ -23,8 +23,13 @@
#include <GrantleeTheme/GrantleeKi18nLocalizer>
#include <GrantleeTheme/GrantleeThemeEngine>
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#include <grantlee/context.h>
#include <grantlee/template.h>
#else
#include <KTextTemplate/context.h>
#include <KTextTemplate/template.h>
#endif
#include <QGpgME/DecryptJob>
#include <QGpgME/Protocol>
...
...
@@ -88,17 +93,32 @@ bool ApplicationGnuPGWKSFormatter::render(const MimeTreeParser::MessagePartPtr &
if
(
hasError
)
{
mp
->
nodeHelper
()
->
setProperty
(
propertyName
.
constData
(),
QVariant
());
}
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
GrantleeTheme
::
Engine
engine
;
#else
KTextTemplate
::
Engine
engine
;
#endif
engine
.
localizer
()
->
setApplicationDomain
(
QByteArrayLiteral
(
"messageviewer_application_gnupgwks_plugin"
));
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
auto
loader
=
QSharedPointer
<
Grantlee
::
FileSystemTemplateLoader
>::
create
();
#else
auto
loader
=
QSharedPointer
<
KTextTemplate
::
FileSystemTemplateLoader
>::
create
();
#endif
loader
->
setTemplateDirs
({
QStringLiteral
(
":/"
)});
engine
.
addTemplateLoader
(
loader
);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Grantlee
::
Template
tpl
=
engine
.
loadByName
(
QStringLiteral
(
"gnupgwksmessagepart.html"
));
#else
KTextTemplate
::
Template
tpl
=
engine
.
loadByName
(
QStringLiteral
(
"gnupgwksmessagepart.html"
));
#endif
if
(
tpl
->
error
())
{
qCWarning
(
GNUPGWKS_LOG
)
<<
tpl
->
errorString
();
}
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Grantlee
::
Context
ctx
;
#else
KTextTemplate
::
Context
ctx
;
#endif
ctx
.
setLocalizer
(
engine
.
localizer
());
QObject
block
;
...
...
@@ -128,8 +148,11 @@ bool ApplicationGnuPGWKSFormatter::render(const MimeTreeParser::MessagePartPtr &
style
.
setProperty
(
"buttonFg"
,
p
.
color
(
QPalette
::
ButtonText
).
name
());
style
.
setProperty
(
"errorFg"
,
MessageCore
::
ColorUtil
::
self
()
->
pgpSignedBadTextColor
().
name
());
ctx
.
insert
(
QStringLiteral
(
"style"
),
&
style
);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Grantlee
::
OutputStream
s
(
htmlWriter
->
stream
());
#else
KTextTemplate
::
OutputStream
s
(
htmlWriter
->
stream
());
#endif
tpl
->
render
(
&
s
,
&
ctx
);
return
true
;
}
plugins/messageviewer/bodypartformatter/highlighter/texthighlighterplugin.cpp
View file @
a018cfa4
...
...
@@ -19,7 +19,11 @@
#include <KSyntaxHighlighting/Repository>
#include <KSyntaxHighlighting/Theme>
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#include <grantlee/template.h>
#else
#include <KTextTemplate/template.h>
#endif
#include <QGuiApplication>
#include <QMimeDatabase>
...
...
@@ -55,6 +59,7 @@ public:
auto
c
=
MessageViewer
::
MessagePartRendererManager
::
self
()
->
createContext
();
c
.
insert
(
QStringLiteral
(
"block"
),
msgPart
.
data
());
c
.
insert
(
QStringLiteral
(
"showOnlyOneMimePart"
),
context
->
showOnlyOneMimePart
());
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
c
.
insert
(
QStringLiteral
(
"content"
),
QVariant
::
fromValue
<
MessageViewer
::
GrantleeCallback
>
([
=
](
Grantlee
::
OutputStream
*
)
{
Highlighter
highLighter
(
htmlWriter
->
stream
());
highLighter
.
setDefinition
(
def
);
...
...
@@ -63,9 +68,23 @@ public:
:
mRepo
.
defaultTheme
(
KSyntaxHighlighting
::
Repository
::
LightTheme
));
highLighter
.
highlight
(
msgPart
->
text
());
}));
#else
c
.
insert
(
QStringLiteral
(
"content"
),
QVariant
::
fromValue
<
MessageViewer
::
GrantleeCallback
>
([
=
](
KTextTemplate
::
OutputStream
*
)
{
Highlighter
highLighter
(
htmlWriter
->
stream
());
highLighter
.
setDefinition
(
def
);
highLighter
.
setTheme
(
QGuiApplication
::
palette
().
color
(
QPalette
::
Base
).
lightness
()
<
128
?
mRepo
.
defaultTheme
(
KSyntaxHighlighting
::
Repository
::
DarkTheme
)
:
mRepo
.
defaultTheme
(
KSyntaxHighlighting
::
Repository
::
LightTheme
));
highLighter
.
highlight
(
msgPart
->
text
());
}));
#endif
auto
t
=
MessageViewer
::
MessagePartRendererManager
::
self
()
->
loadByName
(
QStringLiteral
(
"textmessagepart.html"
));
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Grantlee
::
OutputStream
s
(
htmlWriter
->
stream
());
#else
KTextTemplate
::
OutputStream
s
(
htmlWriter
->
stream
());
#endif
t
->
render
(
&
s
,
&
c
);
return
true
;
}
...
...
plugins/messageviewer/bodypartformatter/itinerary/itineraryrenderer.cpp
View file @
a018cfa4
...
...
@@ -33,10 +33,15 @@
#include <KItinerary/TrainTrip>
#include <KColorScheme>
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#include <grantlee/engine.h>
#include <grantlee/metatype.h>
#include <grantlee/template.h>
#else
#include <KTextTemplate/engine.h>
#include <KTextTemplate/metatype.h>
#include <KTextTemplate/template.h>
#endif
#include <prison/Prison>
...
...
@@ -168,6 +173,7 @@ bool ItineraryRenderer::render(const MimeTreeParser::MessagePartPtr &msgPart,
c
.
insert
(
QStringLiteral
(
"data"
),
elems
);
auto
t
=
MessageViewer
::
MessagePartRendererManager
::
self
()
->
loadByName
(
QStringLiteral
(
"org.kde.messageviewer/itinerary/itinerary.html"
));
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
const_cast
<
Grantlee
::
Engine
*>
(
t
->
engine
())
->
addDefaultLibrary
(
QStringLiteral
(
"kitinerary_grantlee_extension"
));
dynamic_cast
<
GrantleeTheme
::
Engine
*>
(
const_cast
<
Grantlee
::
Engine
*>
(
t
->
engine
()))
->
localizer
()
...
...
@@ -177,5 +183,16 @@ bool ItineraryRenderer::render(const MimeTreeParser::MessagePartPtr &msgPart,
qobject_cast
<
GrantleeTheme
::
Engine
*>
(
const_cast
<
Grantlee
::
Engine
*>
(
t
->
engine
()))
->
localizer
()
->
setApplicationDomain
(
QByteArrayLiteral
(
"libmessageviewer"
));
#else
const_cast
<
KTextTemplate
::
Engine
*>
(
t
->
engine
())
->
addDefaultLibrary
(
QStringLiteral
(
"kitinerary_grantlee_extension"
));
dynamic_cast
<
GrantleeTheme
::
Engine
*>
(
const_cast
<
KTextTemplate
::
Engine
*>
(
t
->
engine
()))
->
localizer
()
->
setApplicationDomain
(
QByteArrayLiteral
(
"messageviewer_semantic_plugin"
));
KTextTemplate
::
OutputStream
s
(
htmlWriter
->
stream
());
t
->
render
(
&
s
,
&
c
);
qobject_cast
<
GrantleeTheme
::
Engine
*>
(
const_cast
<
KTextTemplate
::
Engine
*>
(
t
->
engine
()))
->
localizer
()
->
setApplicationDomain
(
QByteArrayLiteral
(
"libmessageviewer"
));
#endif
return
false
;
// yes, false, we want the rest of the email rendered normally after this
}
plugins/messageviewer/bodypartformatter/markdown/textmarkdownplugin.cpp
View file @
a018cfa4
...
...
@@ -19,7 +19,11 @@
#include <QMimeDatabase>
#include <QTextDocument>
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#include <grantlee/template.h>
#else
#include <KTextTemplate/template.h>
#endif
namespace
{
...
...
@@ -46,6 +50,7 @@ public:
auto
c
=
MessageViewer
::
MessagePartRendererManager
::
self
()
->
createContext
();
c
.
insert
(
QStringLiteral
(
"block"
),
msgPart
.
data
());
c
.
insert
(
QStringLiteral
(
"showOnlyOneMimePart"
),
context
->
showOnlyOneMimePart
());
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
c
.
insert
(
QStringLiteral
(
"content"
),
QVariant
::
fromValue
<
MessageViewer
::
GrantleeCallback
>
([
=
](
Grantlee
::
OutputStream
*
)
{
QString
result
;
#ifdef USE_DISCOUNT_LIB
...
...
@@ -59,8 +64,27 @@ public:
#endif
(
*
htmlWriter
->
stream
())
<<
result
;
}));
#else
c
.
insert
(
QStringLiteral
(
"content"
),
QVariant
::
fromValue
<
MessageViewer
::
GrantleeCallback
>
([
=
](
KTextTemplate
::
OutputStream
*
)
{
QString
result
;
#ifdef USE_DISCOUNT_LIB
MarkdownDiscount
engine
;
engine
.
setText
(
msgPart
->
text
());
result
=
engine
.
toHtml
();
#else
MarkdownQTextDocument
engine
;
engine
.
setText
(
msgPart
->
text
());
result
=
engine
.
toHtml
();
#endif
(
*
htmlWriter
->
stream
())
<<
result
;
}));
#endif
auto
t
=
MessageViewer
::
MessagePartRendererManager
::
self
()
->
loadByName
(
QStringLiteral
(
"textmessagepart.html"
));
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Grantlee
::
OutputStream
s
(
htmlWriter
->
stream
());
#else
KTextTemplate
::
OutputStream
s
(
htmlWriter
->
stream
());
#endif
t
->
render
(
&
s
,
&
c
);
return
true
;
}
...
...
plugins/messageviewer/bodypartformatter/ms-tnef/application_ms-tnef.cpp
View file @
a018cfa4
...
...
@@ -30,7 +30,11 @@
#include <ktnef/ktnefmessage.h>
#include <ktnef/ktnefparser.h>
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#include <grantlee/template.h>
#else
#include <KTextTemplate/template.h>
#endif
#include <KIconLoader>
#include <KLocalizedString>
...
...
@@ -88,6 +92,7 @@ public:
auto
c
=
MessageViewer
::
MessagePartRendererManager
::
self
()
->
createContext
();
c
.
insert
(
QStringLiteral
(
"block"
),
msgPart
.
data
());
c
.
insert
(
QStringLiteral
(
"showOnlyOneMimePart"
),
context
->
showOnlyOneMimePart
());
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
c
.
insert
(
QStringLiteral
(
"content"
),
QVariant
::
fromValue
<
MessageViewer
::
GrantleeCallback
>
([
&
](
Grantlee
::
OutputStream
*
stream
)
{
const
auto
tnefatts
=
parser
.
message
()
->
attachmentList
();
if
(
tnefatts
.
isEmpty
()
&&
inviteStr
.
isEmpty
())
{
...
...
@@ -100,6 +105,49 @@ public:
(
*
stream
)
<<
inviteStr
;
}
const
int
numberOfTnef
(
tnefatts
.
count
());
for
(
int
i
=
0
;
i
<
numberOfTnef
;
++
i
)
{
KTnef
::
KTNEFAttach
*
att
=
tnefatts
.
at
(
i
);
QString
label
=
att
->
displayName
();
if
(
label
.
isEmpty
())
{
label
=
att
->
name
();
}
label
=
MessageCore
::
StringUtil
::
quoteHtmlChars
(
label
,
true
);
const
QString
dir
=
mp
->
nodeHelper
()
->
createTempDir
(
QLatin1String
(
"ktnef-"
)
+
QString
::
number
(
i
));
if
(
!
parser
.
extractFileTo
(
att
->
name
(),
dir
))
{
qCDebug
(
MS_TNEF_LOG
)
<<
"No possible to extract file:"
<<
att
->
name
();
}
// falling back to internal TNEF attachement name if no filename is given for the attached file
// this follows the logic of KTNEFParser::extractFileTo(...)
QString
attFileName
=
att
->
fileName
();
if
(
attFileName
.
isEmpty
())
{
attFileName
=
att
->
name
();
}
mp
->
nodeHelper
()
->
addTempFile
(
dir
+
QLatin1Char
(
'/'
)
+
attFileName
);
const
QString
href
=
QLatin1String
(
"file:"
)
+
dir
+
QLatin1Char
(
'/'
)
+
attFileName
;
const
QString
iconName
=
QUrl
::
fromLocalFile
(
MessageViewer
::
Util
::
iconPathForMimetype
(
att
->
mimeTag
(),
KIconLoader
::
Desktop
,
attFileName
)).
url
();
(
*
stream
)
<<
QStringLiteral
(
"<div><a href=
\"
"
)
<<
href
<<
QStringLiteral
(
"
\"
><img src=
\"
"
)
<<
iconName
<<
QStringLiteral
(
"
\"
border=
\"
0
\"
style=
\"
max-width: 100%
\"
/>"
)
<<
label
<<
QStringLiteral
(
"</a></div><br/>"
);
}
}));
#else
c
.
insert
(
QStringLiteral
(
"content"
),
QVariant
::
fromValue
<
MessageViewer
::
GrantleeCallback
>
([
&
](
KTextTemplate
::
OutputStream
*
stream
)
{
const
auto
tnefatts
=
parser
.
message
()
->
attachmentList
();
if
(
tnefatts
.
isEmpty
()
&&
inviteStr
.
isEmpty
())
{
qCDebug
(
MS_TNEF_LOG
)
<<
"No attachments or invitation found in"
<<
fileName
;
(
*
stream
)
<<
QStringLiteral
(
" <"
)
<<
i18nc
(
"TNEF attachment has no content"
,
"empty"
)
<<
QStringLiteral
(
">"
);
return
;
}
if
(
!
inviteStr
.
isEmpty
())
{
(
*
stream
)
<<
inviteStr
;
}
const
int
numberOfTnef
(
tnefatts
.
count
());
for
(
int
i
=
0
;
i
<
numberOfTnef
;
++
i
)
{
KTnef
::
KTNEFAttach
*
att
=
tnefatts
.
at
(
i
);
...
...
@@ -131,8 +179,13 @@ public:
}
}));
#endif
auto
t
=
MessageViewer
::
MessagePartRendererManager
::
self
()
->
loadByName
(
QStringLiteral
(
"textmessagepart.html"
));
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Grantlee
::
OutputStream
s
(
htmlWriter
->
stream
());
#else
KTextTemplate
::
OutputStream
s
(
htmlWriter
->
stream
());
#endif
t
->
render
(
&
s
,
&
c
);
return
true
;
}
...
...
plugins/messageviewer/bodypartformatter/pkpass/pkpass_plugin.cpp
View file @
a018cfa4
...
...
@@ -14,8 +14,13 @@
#include <KPkPass/Barcode>
#include <KPkPass/BoardingPass>
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#include <grantlee/metatype.h>
#include <grantlee/template.h>
#else
#include <KTextTemplate/metatype.h>
#include <KTextTemplate/template.h>
#endif
#include <prison/Prison>
...
...
@@ -133,8 +138,11 @@ public:
auto
c
=
MessageViewer
::
MessagePartRendererManager
::
self
()
->
createContext
();
c
.
insert
(
QStringLiteral
(
"block"
),
mp
.
data
());
c
.
insert
(
QStringLiteral
(
"pass"
),
pass
.
get
());
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Grantlee
::
Template
t
;
#else
KTextTemplate
::
Template
t
;
#endif
if
(
qobject_cast
<
KPkPass
::
BoardingPass
*>
(
pass
.
get
()))
{
t
=
MessageViewer
::
MessagePartRendererManager
::
self
()
->
loadByName
(
QStringLiteral
(
"org.kde.messageviewer/pkpass/boardingpass.html"
));
}
else
if
(
pass
->
type
()
==
KPkPass
::
Pass
::
EventTicket
)
{
...
...
@@ -145,8 +153,11 @@ public:
// unknown pass type we have no template for
return
false
;
}
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Grantlee
::
OutputStream
s
(
htmlWriter
->
stream
());
#else
KTextTemplate
::
OutputStream
s
(
htmlWriter
->
stream
());
#endif
t
->
render
(
&
s
,
&
c
);
return
true
;
}
...
...
plugins/messageviewer/grantlee/grantleeextension.cpp
View file @
a018cfa4
...
...
@@ -9,8 +9,13 @@
#include <KContacts/Address>
#include <KItinerary/Place>
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#include <grantlee/exception.h>
#include <grantlee/parser.h>
#else
#include <KTextTemplate/exception.h>
#include <KTextTemplate/parser.h>
#endif
#include <QDateTime>
#include <QTimeZone>
...
...
@@ -92,11 +97,18 @@ TagLibrary::TagLibrary(QObject *parent)
:
QObject
(
parent
)
{
}
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QHash
<
QString
,
Grantlee
::
Filter
*>
TagLibrary
::
filters
(
const
QString
&
name
)
#else
QHash
<
QString
,
KTextTemplate
::
Filter
*>
TagLibrary
::
filters
(
const
QString
&
name
)
#endif
{
Q_UNUSED
(
name
)
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QHash
<
QString
,
Grantlee
::
Filter
*>
filters
;
#else
QHash
<
QString
,
KTextTemplate
::
Filter
*>
filters
;
#endif
filters
.
insert
(
QStringLiteral
(
"formatAddress"
),
new
AddressFormatter
());
filters
.
insert
(
QStringLiteral
(
"formatDate"
),
new
DateFormatter
());
filters
.
insert
(
QStringLiteral
(
"formatDateTime"
),
new
DateTimeFormatter
());
...
...
plugins/messageviewer/grantlee/grantleeextension.h
View file @
a018cfa4
...
...
@@ -5,44 +5,75 @@
*/
#pragma once
#include <QObject>
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#include <grantlee/filter.h>
#include <grantlee/taglibraryinterface.h>
#else
#include <KTextTemplate/filter.h>
#include <KTextTemplate/taglibraryinterface.h>
#endif
#include <QObject>
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
class
AddressFormatter
:
public
Grantlee
::
Filter
#else
class
AddressFormatter
:
public
KTextTemplate
::
Filter
#endif
{
public:
QVariant
doFilter
(
const
QVariant
&
input
,
const
QVariant
&
arg
,
bool
autoescape
)
const
override
;
bool
isSafe
()
const
override
;
};
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
class
DateFormatter
:
public
Grantlee
::
Filter
#else
class
DateFormatter
:
public
KTextTemplate
::
Filter
#endif
{
public:
QVariant
doFilter
(
const
QVariant
&
input
,
const
QVariant
&
arg
,
bool
autoescape
)
const
override
;
};
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
class
DateTimeFormatter
:
public
Grantlee
::
Filter
#else
class
DateTimeFormatter
:
public
KTextTemplate
::
Filter
#endif
{
public:
QVariant
doFilter
(
const
QVariant
&
input
,
const
QVariant
&
arg
,
bool
autoescape
)
const
override
;
};
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
class
TimeFormatter
:
public
Grantlee
::
Filter
#else
class
TimeFormatter
:
public
KTextTemplate
::
Filter
#endif
{
public:
QVariant
doFilter
(
const
QVariant
&
input
,
const
QVariant
&
arg
,
bool
autoescape
)
const
override
;
};
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
class
TagLibrary
:
public
QObject
,
public
Grantlee
::
TagLibraryInterface
#else
class
TagLibrary
:
public
QObject
,
public
KTextTemplate
::
TagLibraryInterface
#endif
{
Q_OBJECT
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Q_INTERFACES
(
Grantlee
::
TagLibraryInterface
)
#else
Q_INTERFACES
(
KTextTemplate
::
TagLibraryInterface
)
#endif
Q_PLUGIN_METADATA
(
IID
"org.grantlee.TagLibraryInterface"
)
public:
explicit
TagLibrary
(
QObject
*
parent
=
nullptr
);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QHash
<
QString
,
Grantlee
::
Filter
*>
filters
(
const
QString
&
name
)
override
;
#else
QHash
<
QString
,
KTextTemplate
::
Filter
*>
filters
(
const
QString
&
name
)
override
;
#endif
};
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