Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Projets publics
Sympa
Commits
16ebe7f7
Unverified
Commit
16ebe7f7
authored
Dec 07, 2020
by
IKEDA Soji
Committed by
GitHub
Dec 07, 2020
Browse files
Merge pull request #1038 from ikedas/limited_pdersonalization-trial2 by ikedas
Limited personalization
parents
10ee7b42
b6a5e80b
Changes
11
Hide whitespace changes
Inline
Side-by-side
default/edit_list.conf
View file @
16ebe7f7
...
...
@@ -156,6 +156,8 @@ max_list_members editor hidden
message_hook
owner
hidden
message_hook
privileged_owner
read
personalization
owner
,
privileged_owner
read
default
privileged_owner
write
default
owner
write
default
editor
read
...
...
default/web_tt2/compose_mail.tt2
View file @
16ebe7f7
...
...
@@ -53,7 +53,7 @@
<textarea name="body" id="body" cols="80" rows="25">[% body %]</textarea>
<br />
[% IF merge_feature %]
[% IF
listconf.
merge_feature
== 'on'
%]
<br />
<b>[%|loc%]Messages customization: use the template syntax:[%END%] <a href="http://www.tt2.org">TT2</a></b>
<br />
...
...
src/bin/upgrade_bulk_spool.pl.in
View file @
16ebe7f7
...
...
@@ -214,7 +214,8 @@ sub process {
if
$row_mailer
->
{
verp_bulkmailer
};
$message
->
{
shelved
}{
tracking
}
=
$row_mailer
->
{
tracking_bulkmailer
}
if
$row_mailer
->
{
tracking_bulkmailer
};
$message
->
{
shelved
}{
merge
}
=
1
if
$row_mailer
->
{
merge_bulkmailer
};
# Compatibility: On earlier version only 'all' mode was available.
$message
->
{
shelved
}{
merge
}
=
'
all
'
if
$row_mailer
->
{
merge_bulkmailer
};
# Not a typo: column name was recEipients_bulkmailer.
my
$rcpt_string
=
$row_mailer
->
{
receipients_bulkmailer
};
...
...
src/cgi/wwsympa.fcgi.in
View file @
16ebe7f7
...
...
@@ -14562,8 +14562,8 @@ sub do_compose_mail {
$param->{'topic_required'} = $list->is_msg_topic_tagging_required();
}
$param->{'merge_feature'} =
Sympa::Tools::Data::smart_eq($list->{'admin'}{'merge_feature'}, 'on');
#
$param->{'merge_feature'} =
#
Sympa::Tools::Data::smart_eq($list->{'admin'}{'merge_feature'}, 'on');
return 1;
}
...
...
@@ -14864,9 +14864,10 @@ sub do_send_mail {
$message->reformat_utf8_message([], $charset);
}
## Roughly check TT2 syntax for merge_feature.
if (Sympa::Tools::Data::smart_eq($list->{'admin'}{'merge_feature'}, 'on'))
{
# Roughly check TT2 syntax for personalization.
if ( 'on' eq ($list->{'admin'}{'personalization_feature'} || 'off')
and 'all' eq
($list->{'admin'}{'personalization'}{'web_apply_on'} || 'none')) {
my $new_message = $message->dup;
unless (defined $new_message->personalize($list)) {
# FIXME: Get last_error of template object.
...
...
@@ -14905,9 +14906,12 @@ sub do_send_mail {
# - Add footer / header.
$u_message->prepare_message_according_to_mode('mail', $list);
# - Shelve personalization.
$u_message->{shelved}{merge} = 1
if Sympa::Tools::Data::smart_eq($list->{'admin'}{'merge_feature'},
'on');
if ( 'on' eq ($list->{'admin'}{'personalization_feature'} || 'off')
and 'none' ne
($list->{'admin'}{'personalization'}{'web_apply_on'} || 'none')) {
$u_message->{shelved}{merge} =
$list->{'admin'}{'personalization'}{'web_apply_on'};
}
$u_message->{envelope_sender} = Sympa::get_address($robot, 'owner');
$u_message->{priority} =
...
...
src/lib/Conf.pm
View file @
16ebe7f7
...
...
@@ -1737,6 +1737,11 @@ sub _load_config_file_to_hash {
$keyword
=
$
Sympa::Config::Schema::
obsolete_robot_params
{
$keyword
}
//
$keyword
;
# Resolve renamed parameters FIXME
$keyword
=
{
merge_feature
=>
'
personalization_feature
',
# 6.0b.2 - 6.2.59b.1
}
->
{
$keyword
}
//
$keyword
;
if
(
exists
$params
{
$keyword
}
&&
defined
$params
{
$keyword
}{'
multiple
'}
...
...
src/lib/Sympa/Config/Schema.pm
View file @
16ebe7f7
...
...
@@ -1126,8 +1126,8 @@ our %pinfo = (
},
'
max-size
'
=>
{
obsolete
=>
'
max_size
'},
m
er
ge
_feature
=>
{
context
=>
[
qw(list site)
],
p
er
sonalization
_feature
=>
{
context
=>
[
qw(list
domain
site)
],
order
=>
20.16
,
group
=>
'
sending
',
# outgoing / sending?
gettext_id
=>
"
Allow message personalization
",
...
...
@@ -1135,6 +1135,36 @@ our %pinfo = (
occurrence
=>
'
1
',
default
=>
'
off
',
},
merge_feature
=>
{
obsolete
=>
'
personalization_feature
'},
personalization
=>
{
context
=>
[
qw(list domain site)
],
order
=>
20.161
,
group
=>
'
sending
',
gettext_id
=>
"
Message personalization
",
format
=>
{
web_apply_on
=>
{
context
=>
[
qw(list domain site)
],
order
=>
1
,
group
=>
'
sending
',
gettext_id
=>
'
which part of message from web conversion is applied
',
format
=>
['
none
',
'
footer
',
'
all
'],
default
=>
'
footer
',
occurrence
=>
'
1
'
},
mail_apply_on
=>
{
context
=>
[
qw(list domain site)
],
order
=>
2
,
group
=>
'
sending
',
gettext_id
=>
'
which part of e-mail message conversion is applied
',
format
=>
['
none
',
'
footer
',
'
all
'],
default
=>
'
none
',
occurrence
=>
'
1
'
},
},
},
message_hook
=>
{
context
=>
[
qw(list)
],
...
...
src/lib/Sympa/ListOpt.pm
View file @
16ebe7f7
...
...
@@ -44,7 +44,8 @@ our %list_option = (
'
list
'
=>
{'
gettext_id
'
=>
'
list
'},
# include_ldap_2level_query.select2, include_ldap_2level_query.select1,
# include_ldap_query.select, reply_to_header.value, dmarc_protection.mode
# include_ldap_query.select, reply_to_header.value, dmarc_protection.mode,
# personalization.web_apply_on, personalization.mail_apply_on
'
all
'
=>
{'
gettext_id
'
=>
'
all
'},
# reply_to_header.value
...
...
@@ -55,10 +56,14 @@ our %list_option = (
'
body
'
=>
{'
gettext_id
'
=>
'
message body
'},
'
subject_and_body
'
=>
{'
gettext_id
'
=>
'
subject and body
'},
# personalization.web_apply_on, personalization.mail_apply_on
'
footer
'
=>
{'
gettext_id
'
=>
'
header and footer
'},
# bouncers_level2.notification, bouncers_level2.action,
# bouncers_level1.notification, bouncers_level1.action,
# spam_protection, dkim_signature_apply_on, web_archive_spam_protection,
# dmarc_protection.mode, automatic_list_removal
# dmarc_protection.mode, automatic_list_removal,
# personalization.web_apply_on, personalization.mail_apply_on
'
none
'
=>
{'
gettext_id
'
=>
'
do nothing
'},
# automatic_list_removal
...
...
src/lib/Sympa/Message.pm
View file @
16ebe7f7
...
...
@@ -1393,7 +1393,6 @@ sub personalize {
my
$self
=
shift
;
my
$list
=
shift
;
my
$rcpt
=
shift
||
undef
;
my
$data
=
shift
||
{};
my
$content_type
=
lc
(
$self
->
{
_head
}
->
mime_attr
('
Content-Type
')
||
'');
if
(
$content_type
eq
'
multipart/encrypted
'
...
...
@@ -1406,8 +1405,23 @@ sub personalize {
my
$entity
=
$self
->
as_entity
->
dup
;
# Initialize parameters at first only once.
$data
->
{'
headers
'}
||=
{};
my
$data
=
$self
->
_personalize_attrs
;
unless
(
defined
_merge_msg
(
$entity
,
$list
,
$rcpt
,
$data
))
{
return
undef
;
}
$self
->
set_entity
(
$entity
);
return
$self
;
}
sub
_personalize_attrs
{
my
$self
=
shift
;
my
$entity
=
$self
->
as_entity
;
my
$headers
=
$entity
->
head
;
my
$data
=
{
headers
=>
{}};
foreach
my
$key
(
qw/subject x-originating-ip message-id date x-original-to from to thread-topic content-type/
)
{
...
...
@@ -1415,16 +1429,11 @@ sub personalize {
my
$value
=
$headers
->
get
(
$key
,
0
);
chomp
$value
;
$value
=~
s/(?:\r\n|\r|\n)(?=[ \t])//g
;
# unfold
$data
->
{'
headers
'}{
$key
}
=
$value
;
}
$data
->
{'
subject
'}
=
$self
->
{'
decoded_subject
'};
unless
(
defined
_merge_msg
(
$entity
,
$list
,
$rcpt
,
$data
))
{
return
undef
;
$data
->
{
headers
}{
$key
}
=
$value
;
}
$data
->
{
subject
}
=
$self
->
{
decoded_subject
};
$self
->
set_entity
(
$entity
);
return
$self
;
return
$data
;
}
sub
_merge_msg
{
...
...
@@ -1581,7 +1590,7 @@ sub personalize_text {
my
$message_output
;
my
$user
=
$list
->
get_list_member
(
$rcpt
);
my
$user
=
$list
->
get_list_member
(
$rcpt
)
if
$rcpt
;
if
(
$user
)
{
$user
->
{'
escaped_email
'}
=
URI::Escape::
uri_escape
(
$rcpt
);
...
...
@@ -1599,9 +1608,18 @@ sub personalize_text {
# Parse the template in the message : replace the tags and the parameters
# by the corresponding values
my
$template
=
Sympa::
Template
->
new
(
undef
);
return
undef
unless
$template
->
parse
(
$data
,
\
$body
,
\
$message_output
,
is_not_template
=>
1
);
unless
(
$template
->
parse
(
$data
,
\
$body
,
\
$message_output
,
is_not_template
=>
1
)
)
{
$log
->
syslog
(
'
err
',
'
Failed parsing template: %s
',
$template
->
{
last_error
}
);
return
undef
;
}
return
$message_output
;
}
...
...
@@ -1632,9 +1650,10 @@ sub prepare_message_according_to_mode {
if
(
_as_singlepart
(
$entity
,
'
text/plain
'))
{
$log
->
syslog
('
notice
',
'
Multipart message changed to singlepart
');
}
## Add a footer
_decorate_parts
(
$entity
,
$list
);
$self
->
set_entity
(
$entity
);
# Add a footer
$self
->
{
shelved
}{
decorate
}
=
1
;
}
elsif
(
$mode
eq
'
urlize
')
{
# Prepare message for urlize reception mode.
# Not extract message/rfc822 parts.
...
...
@@ -1649,44 +1668,40 @@ sub prepare_message_according_to_mode {
my
$entity
=
$parser
->
parse_data
(
$msg_string
);
_urlize_parts
(
$entity
,
$list
,
$self
->
{'
message_id
'});
## Add a footer
_decorate_parts
(
$entity
,
$list
);
$self
->
set_entity
(
$entity
);
# Add a footer
$self
->
{
shelved
}{
decorate
}
=
1
;
}
else
{
# 'mail'
# Prepare message for normal reception mode,
# and add a footer.
unless
(
$self
->
{'
protected
'})
{
my
$entity
=
$self
->
as_entity
->
dup
;
_decorate_parts
(
$entity
,
$list
);
$self
->
set_entity
(
$entity
);
}
$self
->
{
shelved
}{
decorate
}
=
1
unless
$self
->
{'
protected
'};
}
return
$self
;
}
# OBSOLETED. Use prepare_message_according_to_mode('mail').
# Old name:
# Sympa::List::add_parts() or Message::add_parts(), n.b. not add_part().
# Sympa::Message::_decorate_parts().
sub
decorate
{
my
$self
=
shift
;
$log
->
syslog
('
debug3
',
'
(%s, %s, %s => %s)
',
@
_
);
my
$self
=
shift
;
my
$list
=
shift
;
my
$rcpt
=
shift
;
my
%options
=
@_
;
return
$self
->
prepare_message_according_to_mode
('
mail
',
$self
->
{
context
});
}
return
unless
ref
$list
eq
'
Sympa::List
';
# Old name:
# Sympa::List::add_parts() or Message::add_parts(), n.b. not add_part().
sub
_decorate_parts
{
$log
->
syslog
('
debug3
',
'
(%s, %s)
');
my
$entity
=
shift
;
my
$list
=
shift
;
my
$entity
=
$self
->
as_entity
->
dup
;
my
$mode
=
$options
{
mode
}
||
'';
my
$type
=
$list
->
{'
admin
'}{'
footer_type
'};
my
$eff_type
=
$entity
->
effective_type
||
'
text/plain
';
## Signed or encrypted messages won't be modified.
if
(
$eff_type
=~
/^multipart\/(signed|encrypted)$/i
)
{
return
$entity
;
}
return
1
if
$eff_type
=~
/^multipart\/(signed|encrypted)$/i
;
my
$header
=
(
$type
eq
'
mime
')
...
...
@@ -1707,30 +1722,28 @@ sub _decorate_parts {
or $footer and -s $
footer
or
$global_footer
and
-
s $global_footer;
my $data;
if ($mod
e
)
{
$data
=
$self
->
_personalize_attrs
;
}
if
(
$type
eq
'
append
')
{
## append footer/header
my ($glo
bal_footer_text
,
$footer_text
,
$header_text
)
=
('',
'',
'');
if
(
$header
and
-
s $header) {
if (open my $fh, '<', $
header
)
{
$header_text
=
do
{
local
$RS
;
<
$fh
>
};
close
$fh
;
}
$header_text
=
''
unless
$header_text
=~
/\S/
;
}
if
(
$footer
and
-
s $footer) {
if (open my $fh, '<', $
footer
)
{
$footer_text
=
do
{
local
$RS
;
<
$fh
>
};
close
$fh
;
}
$footer_text
=
''
unless
$footer_text
=~
/\S/
;
}
if
(
$global_footer
and
-
s $global_footer) {
if (open my $fh, '<', $glo
bal_footer
)
{
$global_footer_text
=
do
{
local
$RS
;
<
$fh
>
};
close
$fh
;
}
$global_footer_text
=
''
unless
$global_footer_text
=~
/\S/
;
}
# append footer/header
my
$header_text
=
_footer_text
(
$header
,
$list
,
$rcpt
,
$data
,
mode
=>
$mode
,
type
=>
'
header
'
)
//
'';
my
$footer_text
=
_footer_text
(
$footer
,
$list
,
$rcpt
,
$data
,
mode
=>
$mode
,
type
=>
'
footer
'
)
//
'';
my
$global_footer_text
=
_footer_text
(
$global_footer
,
$list
,
$rcpt
,
$data
,
mode
=>
$mode
,
type
=>
'
global footer
'
)
//
'';
if
(
length
$header_text
or
length
$footer_text
or
length
$global_footer_text
)
{
...
...
@@ -1745,112 +1758,61 @@ sub _decorate_parts {
}
}
else
{
## MIME footer/header
my
$parser
=
MIME::
Parser
->
new
;
$parser
->
output_to_core
(
1
);
$parser
->
tmp_dir
(
$
Conf::
Conf
{'
tmpdir
'});
if
(
$eff_type
=~
/^multipart\/alternative/i
||
$eff_type
=~
/^multipart\/related/i
)
{
$log
->
syslog
('
debug3
',
'
Making message %s into multipart/mixed
',
$entity
);
$entity
->
make_multipart
("
mixed
",
Force
=>
1
);
}
if
(
$header
and
-
s $header) {
my $fh;
unless (open $
fh
,
'
<
',
$header
)
{
;
}
elsif
(
$header
=~
/\.mime$/
)
{
my
$header_part
;
eval
{
$header_part
=
$parser
->
parse
(
$fh
);
};
close
$fh
;
if
(
$EVAL_ERROR
)
{
$log
->
syslog
('
err
',
'
Failed to parse MIME data %s: %s
',
$header
,
$parser
->
last_error
);
}
else
{
$entity
->
make_multipart
unless
$entity
->
is_multipart
;
## Add AS FIRST PART (0)
$entity
->
add_part
(
$header_part
,
0
);
}
}
else
{
## text/plain header
$entity
->
make_multipart
unless
$entity
->
is_multipart
;
my
$header_text
=
do
{
local
$RS
;
<
$fh
>
};
close
$fh
;
my
$header_part
=
MIME::
Entity
->
build
(
Data
=>
$header_text
,
Type
=>
"
text/plain
",
Filename
=>
undef
,
'
X-Mailer
'
=>
undef
,
Encoding
=>
"
8bit
",
Charset
=>
"
UTF-8
"
);
$entity
->
add_part
(
$header_part
,
0
);
}
_add_footer_part(
$entity, $
header
,
$list
,
$rcpt
,
$data
,
mode
=>
$mode
,
type
=>
'
header
',
prepend
=>
1
);
}
if
(
$footer
and
-
s $footer) {
my $fh;
unless (open $
fh
,
'
<
',
$footer
)
{
;
}
elsif
(
$footer
=~
/\.mime$/
)
{
my
$footer_part
;
eval
{
$footer_part
=
$parser
->
parse
(
$fh
);
};
close
$fh
;
if
(
$EVAL_ERROR
)
{
$log
->
syslog
('
err
',
'
Failed to parse MIME data %s: %s
',
$footer
,
$parser
->
last_error
);
}
else
{
$entity
->
make_multipart
unless
$entity
->
is_multipart
;
$entity
->
add_part
(
$footer_part
);
}
}
else
{
## text/plain footer
$entity
->
make_multipart
unless
$entity
->
is_multipart
;
my
$footer_text
=
do
{
local
$RS
;
<
$fh
>
};
close
$fh
;
$entity
->
attach
(
Data
=>
$footer_text
,
Type
=>
"
text/plain
",
Filename
=>
undef
,
'
X-Mailer
'
=>
undef
,
Encoding
=>
"
8bit
",
Charset
=>
"
UTF-8
"
);
}
_add_footer_part(
$entity, $
footer
,
$list
,
$rcpt
,
$data
,
mode
=>
$mode
,
type
=>
'
footer
'
);
}
if
(
$global_footer
and
-
s $global_footer) {
my $fh;
unless (open $
fh
,
'
<
',
$global_footer
)
{
;
}
elsif
(
$global_footer
=~
/\.mime$/
)
{
my
$global_footer_part
;
eval
{
$global_footer_part
=
$parser
->
parse
(
$fh
);
};
close
$fh
;
if
(
$EVAL_ERROR
)
{
$log
->
syslog
('
err
',
'
Failed to parse MIME data %s: %s
',
$global_footer
,
$parser
->
last_error
);
}
else
{
$entity
->
make_multipart
unless
$entity
->
is_multipart
;
$entity
->
add_part
(
$global_footer_part
);
}
}
else
{
## text/plain global_footer
$entity
->
make_multipart
unless
$entity
->
is_multipart
;
my
$global_footer_text
=
do
{
local
$RS
;
<
$fh
>
};
close
$fh
;
$entity
->
attach
(
Data
=>
$global_footer_text
,
Type
=>
"
text/plain
",
Filename
=>
undef
,
'
X-Mailer
'
=>
undef
,
Encoding
=>
"
8bit
",
Charset
=>
"
UTF-8
"
);
}
_add_footer_part(
$entity, $glo
bal_footer
,
$list
,
$rcpt
,
$data
,
mode
=>
$mode
,
type
=>
'
global footer
'
);
}
}
return
$entity
;
$self
->
set_entity
(
$entity
);
return
1
;
}
sub
_footer_text
{
my
$footer
=
shift
;
my
$list
=
shift
;
my
$rcpt
=
shift
;
my
$data
=
shift
;
my
%options
=
@_
;
my
$mode
=
$options
{
mode
};
my
$type
=
$options
{
type
};
my
$footer_text
=
'';
if
(
$footer
and
-
s $footer) {
if (open my $fh, '<', $
footer
)
{
$footer_text
=
do
{
local
$RS
;
<
$fh
>
};
close
$fh
;
}
if
(
$mode
)
{
$footer_text
=
personalize_text
(
$footer_text
,
$list
,
$rcpt
,
$data
);
unless
(
defined
$footer_text
)
{
$log
->
syslog
('
info
',
'
Error personalizing %s
',
$type
);
$footer_text
=
'';
}
}
$footer_text
=
''
unless
$footer_text
=~
/\S/
;
}
return
$footer_text
;
}
## Append header/footer/global_footer to text/plain body.
...
...
@@ -1958,6 +1920,70 @@ sub _append_parts {
return
undef
;
}
sub
_add_footer_part
{
my
$entity
=
shift
;
my
$footer
=
shift
;
my
$list
=
shift
;
my
$rcpt
=
shift
;
my
$data
=
shift
;
my
%options
=
@_
;
my
$mode
=
$options
{
mode
};
my
$type
=
$options
{
type
};
my
$prepend
=
$options
{
prepend
};
my
$parser
=
MIME::
Parser
->
new
;
$parser
->
output_to_core
(
1
);
$parser
->
tmp_dir
(
$
Conf::
Conf
{'
tmpdir
'});
my
$fh
;
my
$footer_part
;
my
$error
;
unless
(
open
$fh
,
'
<
',
$footer
)
{
return
0
;
}
elsif
(
$footer
=~
/\.mime$/
)
{
eval
{
$footer_part
=
$parser
->
parse
(
$fh
);
};
close
$fh
;
$error
=
$parser
->
last_error
;
}
else
{
# text/plain footer
my
$footer_text
=
do
{
local
$RS
;
<
$fh
>
};
close
$fh
;
eval
{
$footer_part
=
MIME::
Entity
->
build
(
Data
=>
$footer_text
,
Type
=>
"
text/plain
",
Filename
=>
undef
,
'
X-Mailer
'
=>
undef
,
Encoding
=>
"
8bit
",
Charset
=>
"
UTF-8
"
);
};
$error
=
$EVAL_ERROR
;
}
my
$eff_type
=
$entity
->
effective_type
||
'
text/plain
';
unless
(
$footer_part
)
{
$log
->
syslog
('
err
',
'
Failed to parse MIME data %s: %s
',
$footer
,
$error
);
}
elsif
(
$mode
and
not
defined
_merge_msg
(
$footer_part
,
$list
,
$rcpt
,
$data
))
{
$log
->
syslog
('
info
',
'
Error personalizing %s
',
$type
);
}
else
{
unless
(
$entity
->
is_multipart
)
{
$entity
->
make_multipart
;
}
elsif
(
$eff_type
=~
/^multipart\/alternative/i
or
$eff_type
=~
/^multipart\/related/i
)
{
$log
->
syslog
('
debug3
',
'
Making message %s into multipart/mixed
',
$entity
);
$entity
->
make_multipart
("
mixed
",
Force
=>
1
);
}
$entity
->
add_part
(
$footer_part
,
$prepend
?
0
:
-
1
);
}
}
# Styles to cancel local CSS.
my
$div_style
=
'
background: transparent; border: none; clear: both; display: block; float: none; position: static
';
...
...
@@ -3953,7 +3979,7 @@ Returns:
C<1> if the message is considered signed.
C<0> otherwise.
=item personalize ( $list, [ $rcpt
], [ $data
] )
=item personalize ( $list, [ $rcpt ] )
I<Instance method>.
Personalizes a message with custom attributes of a user.
...
...
@@ -4040,10 +4066,7 @@ the message is not modified.
Returns modified message object itself, or C<undef> if transformation failed.
=item decorate ( )