Dodan modul za kakovost respondentov - V DELU

This commit is contained in:
pero1203 2021-04-30 10:30:53 +02:00
parent 3f18ef511b
commit 877eb47f03
28 changed files with 1652 additions and 175 deletions

View File

@ -1872,6 +1872,16 @@ class SurveyAdmin
echo '</li>'; echo '</li>';
echo '<li class="space"></li>'; echo '<li class="space"></li>';
# kakovost resp - V DELU - ZAENKRAT SAMO ADMINI
if ($admin_type === '0') {
echo '<li>';
echo '<a class="no-img' . ($_GET['a'] == A_KAKOVOST_RESP ? ' active' : '') . '"'
. ' href="index.php?anketa=' . $this->anketa . '&amp;a=' . A_KAKOVOST_RESP . '" title="' . $lang['srv_kakovost'] . '">';
echo $lang['srv_kakovost'] . '</a>';
echo '</li>';
echo '<li class="space"></li>';
}
# speeder index - V DELU - ZAENKRAT SAMO ADMINI # speeder index - V DELU - ZAENKRAT SAMO ADMINI
if ($admin_type === '0') { if ($admin_type === '0') {
echo '<li>'; echo '<li>';
@ -2336,6 +2346,13 @@ class SurveyAdmin
$SUR->displayUporabnost(); $SUR->displayUporabnost();
echo ' </div>'; echo ' </div>';
} }
// prikaze modul kakovost
elseif ($_GET['a'] == A_KAKOVOST_RESP) {
echo ' <div id="surveyKakovostResp">';
$SUR = new SurveyKakovost($this->anketa);
$SUR->displayKakovost();
echo ' </div>';
}
// Prikaze analizo hitrosti respondenta // Prikaze analizo hitrosti respondenta
elseif ($_GET['a'] == A_SPEEDER_INDEX) { elseif ($_GET['a'] == A_SPEEDER_INDEX) {
echo ' <div id="surveySpeederIndex">'; echo ' <div id="surveySpeederIndex">';
@ -3833,7 +3850,7 @@ class SurveyAdmin
} }
} }
if (($_GET['a'] == A_COLLECT_DATA || $_GET['a'] == A_USABLE_RESP || $_GET['a'] == A_SPEEDER_INDEX || $_GET['a'] == A_REMINDER_TRACKING || $_GET['a'] == A_TEXT_ANALYSIS || $_GET['a'] == A_EDITS_ANALYSIS || $_GET['a'] == A_ANALYSIS) && $_GET['m'] != 'analysis_links' && $_GET['m'] != 'anal_arch') if (($_GET['a'] == A_COLLECT_DATA || $_GET['a'] == A_USABLE_RESP || $_GET['a'] == A_KAKOVOST_RESP || $_GET['a'] == A_SPEEDER_INDEX || $_GET['a'] == A_REMINDER_TRACKING || $_GET['a'] == A_TEXT_ANALYSIS || $_GET['a'] == A_EDITS_ANALYSIS || $_GET['a'] == A_ANALYSIS) && $_GET['m'] != 'analysis_links' && $_GET['m'] != 'anal_arch')
$this->displayExportHover($navigation); $this->displayExportHover($navigation);
} else if ($navigation == 1) { } else if ($navigation == 1) {

View File

@ -35,6 +35,7 @@ class CrossRoad {
break; break;
case A_NONRESPONSE_GRAPH: case A_NONRESPONSE_GRAPH:
case A_USABLE_RESP: case A_USABLE_RESP:
case A_KAKOVOST_RESP:
case A_SPEEDER_INDEX: case A_SPEEDER_INDEX:
case A_TEXT_ANALYSIS: case A_TEXT_ANALYSIS:
case A_GEOIP_LOCATION: case A_GEOIP_LOCATION:

View File

@ -57,6 +57,7 @@
define("A_NONRESPONSE_GRAPH", "nonresponse_graph"); define("A_NONRESPONSE_GRAPH", "nonresponse_graph");
define("A_PARA_GRAPH", "para_graph"); define("A_PARA_GRAPH", "para_graph");
define("A_USABLE_RESP", "usable_resp"); define("A_USABLE_RESP", "usable_resp");
define("A_KAKOVOST_RESP", "kakovost_resp");
define("A_SPEEDER_INDEX", "speeder_index"); define("A_SPEEDER_INDEX", "speeder_index");
define("A_TEXT_ANALYSIS", "text_analysis"); define("A_TEXT_ANALYSIS", "text_analysis");
define("A_GEOIP_LOCATION", "geoip_location"); define("A_GEOIP_LOCATION", "geoip_location");

View File

@ -0,0 +1,72 @@
calc.usability <- function(m.all, return.type){
# return.type:
# 1: return only absolute
# 2: return only %
# 3: return both (even rows: absolute, odd rows: %)
## calculations
m.all[, Prekinitve:=v3]
m.all[, Neodgovori:=v1]
m.all[, Nevsebinski:=v96+v97+v98+v99]
m.all[, Izpostavljen:=allqs-(v2+v3+v4+v5)]
setnames(m.all, "va", "Veljavni")
m.all[, UNL:=Neodgovori/Izpostavljen]
m.all[is.na(UNL)==T, UNL:=0]
m.all[, UML:=(v3/allqs)+(1-(v3/allqs))*UNL]
m.all[, UCL:=1-UML]
m.all[, UIL:=v2/(v2+Izpostavljen)]
m.all[is.na(UIL)==T, UIL:=0]
m.all[, UAQ:=v4/allqs]
m.all[, Uporabnost:=1-UML]
#tidy up
setcolorder(m.all, c("recnum", "allqs", "Veljavni", "Nevsebinski", "Neodgovori",
"Izpostavljen", "Prekinitve", "Uporabnost",
"v1", "v2", "v3", "v4", "v5", "v96", "v97", "v98", "v99",
"UNL", "UML", "UCL", "UIL", "UAQ"))
if(return.type==1){
return(m.all)
}else{
m.all.p <- copy(m.all)
m.all.p[, (c("Veljavni", "Nevsebinski", "Neodgovori")) := lapply(.SD, "/", m.all.p$Izpostavljen), .SDcols=c("Veljavni", "Nevsebinski", "Neodgovori")]
m.all.p[, (c("Prekinitve", "v1", "v2", "v3", "v4", "v5", "v96", "v97", "v98", "v99")) := lapply(.SD, "/", m.all.p$allqs), .SDcols=c("Prekinitve", "v1", "v2", "v3", "v4", "v5", "v96", "v97", "v98", "v99")]
m.all.p[, Izpostavljen:=1]
if(return.type==2){
return(m.all.p)
}else{
m.all[, Uporabnost:=Veljavni]
m.all[, c("UNL", "UML", "UCL", "UIL", "UAQ"):=NA]
m.all <- m.all[, lapply(.SD, as.character)]
m.all.p[, allqs:=NA]
m.all.p[, allqs:=as.character(allqs)]
change.cols <- c("Veljavni", "Nevsebinski", "Neodgovori", "Izpostavljen", "Prekinitve", "Uporabnost",
"v1", "v2", "v3", "v4", "v5", "v96", "v97", "v98", "v99",
"UNL", "UML", "UCL", "UIL", "UAQ")
m.all.p[, (change.cols):=lapply(.SD, function(x){paste0(round(x*100, 0), "%")}), .SD=change.cols]
m.1ka <- data.table(matrix("", nrow=nrow(m.all)*2, ncol=ncol(m.all)))
a.rows <- as.integer(seq(1, nrow(m.1ka), by=2))
p.rows <- as.integer(seq(2, nrow(m.1ka), by=2))
set(m.1ka, a.rows, 1:ncol(m.1ka), value=m.all)
suppressWarnings(set(m.1ka, p.rows, 1:ncol(m.1ka), value=m.all.p))
setnames(m.1ka, colnames(m.all))
m.1ka[, Status:=NA_character_]
setcolorder(m.1ka, c("recnum", "allqs", "Veljavni", "Nevsebinski", "Neodgovori",
"Izpostavljen", "Prekinitve", "Uporabnost", "Status",
"v1", "v2", "v3", "v4", "v5", "v96", "v97", "v98", "v99",
"UNL", "UML", "UCL", "UIL", "UAQ"))
return(m.1ka)
}
}
}

View File

@ -0,0 +1,71 @@
gen.survey.str <- function(colnames.dsa, questions.file, items.file){
#import questions file
questions <- fread(questions.file, skip=1, header=F,
select=c(2, 5, 6, 8, 9, 10),
col.names=c("question.id", "variable", "tip", "size", "visible", "params"))
#create variable list from survey data file
#remove "recnum" and "_text" fields
var.data <- colnames.dsa[sapply(colnames.dsa, function(x){substr(x, nchar(x)-4, nchar(x))})!="_text"]
#create variable list from questions file
var.questions <- questions$variable
#generate data.table from var.data list
survey.str <- data.table(variable = var.data)
setkey(questions, "variable")
setkey(survey.str, "variable")
#if all var.data in var.questions, do the simple merge and return file
if(all(var.data %in% var.questions)){
survey.str <- questions[survey.str,]
return(survey.str)
}else{ #if not, import items file and do additional merge with it...
#import items file
items <- fread(items.file, skip=1, header=F,
select=c(2, 3, 4),
col.names=c("question.id", "item.id", "variable"))
setkey(items, "question.id")
setkey(questions, "question.id")
#bind variables from questions and items (for the later, only take instances with no match in the questions file...)
survey.str.qi <- rbindlist(list(questions[var.questions %in% var.data,],
items[questions[!(var.questions %in% var.data), -"variable", with=F], nomatch=0L]),
fill=T)
#merge questions+items with survey data...
setkey(survey.str.qi, "variable")
setkey(survey.str, "variable")
survey.str <- survey.str.qi[survey.str,]
#if all var.data is now matched, return the survey.str
if(!(any(is.na(survey.str)))){
return(survey.str)
}else{ #if not, do additional merging...
#create index of all NA instaces from survey.str...
index <- apply(cbind(survey.str[, is.na(tip)],
(sapply(survey.str[, variable], function(x){
substr(x, 1, regexpr("\\_[^\\_]*$", x)-1)
}) %in% survey.str.qi$variable)
),
1, all)
#... using regex to find matches among unmatched instances from survey.str.qi
add <- merge(survey.str[index, list(variable, substr(variable, 1, regexpr("\\_[^\\_]*$", variable)-1))],
survey.str.qi[!(variable %in% survey.str$variable),],
by.x="V2", by.y="variable", all.y=F)[, list(question.id, item.id, tip, visible, size, params)]
#update survey.str with new values
survey.str[index, c("question.id", "item.id", "tip", "visible", "size", "params") := as.list(add)]
#if there is no NAs left, return survey.str, else return msg
if(!(any(is.na(survey.str$tip)))){
return(survey.str)
}else{
return(paste("No match found for: ", survey.str[is.na(tip), variable]))
}
}
}
}

View File

@ -0,0 +1,181 @@
gen.usability.matrix <- function(dsa, survey.str){
#define special values to detect
#order of this values is important:
# in case of conflicts @ chk.t types of questions the order sets the priporty of which values to keep
special.v <- c(-1, -3, -5, -96, -97, -98, -99, -4, -2)
#define which variables belong to checkbox-like* questions
#(* i.e.: check for special values @ ANY variable per question/item ID)
# 2: normal checkbox
# 16: multicheckbox
# 17: ranking
chkbox.t <- c(2, 16, 17)
##all other variables belong to normal** questions
#(** i.e.: check for special values @ each variable per question/item ID)
#if there are no normal questions, create 0 matrix, otherwise...
if(nrow(survey.str[!(tip %in% chkbox.t),])==0){
m.n <- matrix(0, nrow = nrow(dsa), ncol=length(special.v)+1)
}else{
#create list of all normal questions
c.n <- colnames(dsa)[which(colnames(dsa) %in% survey.str[!(tip %in% chkbox.t), variable])]
#...count all non-special values for each variable
#... + count each special value for each variable
m.n <- cbind(rowSums(sapply(dsa[, c.n, with=FALSE], function(x){!(x %in% special.v)})),
sapply(special.v, function(x){as.integer(rowSums(dsa[, c.n, with=FALSE]==x, na.rm=TRUE))}))
}
##procedure for tip:2
#only run if there is an at least one tip:2 variable
if(survey.str[, any(tip==2)]){
#get list of all unique tip:2 question ids
q.2 <- unique(survey.str[tip==2, question.id])
#get list of all corresponding variables for each q.2 id
c.2 <- lapply(q.2, function(x){colnames(dsa)[which(colnames(dsa) %in% survey.str[question.id==x & tip==2, variable])]})
#(do this for each instance in c.2):
#for each set of variables:
# check if any variable contains at least one non-special value
# + (for each special value) check if any variable contains at least special value
m.2 <- lapply(c.2, function(x){
cbind(apply(dsa[, x, with=FALSE], 1, function(q){any(!(q %in% special.v))}),
sapply(special.v, function(y){
apply(dsa[, x, with=FALSE], 1, function(q){any(q==y)})
})
)
})
# (do this for each instance in c.2)
# if multiple special values per respondent exist, keep only the first one
m.2 <- lapply(m.2, function(x){
if(any(rowSums(x)>1)){
p <- x[rowSums(x)>1,]
for(i in 1:nrow(p)){
a <- p[i,]
f <- TRUE
for(j in 1:length(a)){
print(j)
if(a[j] & f){
f <- FALSE
}else if(a[j] & !f){
a[j] <- FALSE
}
}
p[i,] <- a
}
x[rowSums(x)>1,] <- p
}else{x}
})
#add to m.n
m.n <- m.n + Reduce('+', m.2)
}
##procedure for tip:16
#only run if there is an at least one tip:16 variable
if(survey.str[, any(tip==16)]){
#get list of all unique tip:16 item ids
q.16 <- unique(survey.str[tip==16, item.id])
#get list of all corresponding variables for each q.16 id
c.16 <- lapply(q.16, function(x){colnames(dsa)[which(colnames(dsa) %in% survey.str[item.id==x & tip==16, variable])]})
#(do this for each special value):
#for each set of variables, check if any variable contains at least one special value
# m.16 <- sapply(special.v, function(x){
# rowSums(sapply(c.16, function(y){
# apply(dsa[, y, with=FALSE], 1, function(q){any(q==x)})
# }))
# })
#(do this for each instance in c.16):
#for each set of variables:
# check if any variable contains at least one non-special value
# + (for each special value) check if any variable contains at least special value
m.16 <- lapply(c.16, function(x){
cbind(apply(dsa[, x, with=FALSE], 1, function(q){any(!(q %in% special.v))}),
sapply(special.v, function(y){
apply(dsa[, x, with=FALSE], 1, function(q){any(q==y)})
})
)
})
# (do this for each instance in c.16)
# if multiple special values per respondent exist, keep only the first one
m.16 <- lapply(m.16, function(x){
if(any(rowSums(x)>1)){
p <- x[rowSums(x)>1,]
for(i in 1:nrow(p)){
a <- p[i,]
f <- TRUE
for(j in 1:length(a)){
print(j)
if(a[j] & f){
f <- FALSE
}else if(a[j] & !f){
a[j] <- FALSE
}
}
p[i,] <- a
}
x[rowSums(x)>1,] <- p
}else{x}
})
m.n <- m.n + Reduce('+', m.16)
}
##procedure for tip:17
#only run if there is an at least one tip:17 variable
if(survey.str[, any(tip==17)]){
#get list of all unique tip:17 question ids
q.17 <- unique(survey.str[tip==17, question.id])
#get list of all corresponding variables for each q.17 id
c.17 <- lapply(q.17, function(x){colnames(dsa)[which(colnames(dsa) %in% survey.str[question.id==x & tip==17, variable])]})
#similiar procedure as for tip:2 and tip:16....
m.17 <- lapply(c.17, function(x){
cbind(apply(dsa[, x, with=FALSE], 1, function(q){any(!(q %in% special.v))}),
sapply(special.v, function(y){
apply(dsa[, x, with=FALSE], 1, function(q){any(q==y)})
})
)
})
#... the only difference is that we are checking for all rowsums > 0, not > 1
m.17 <- lapply(m.17, function(x){
if(any(rowSums(x)>1)){
p <- x[rowSums(x)>0,]
for(i in 1:nrow(p)){
a <- p[i,]
f <- TRUE
for(j in 1:length(a)){
if(a[j] & f){
f <- FALSE
}else if(a[j] & !f){
a[j] <- FALSE
}
}
p[i,] <- a
}
x[rowSums(x)>0,] <- p
}else{x}
})
m.n <- m.n + Reduce('+', m.17)
}
m.n <- cbind(m.n, rowSums(m.n))
if(all(m.n[, ncol(m.n)][1]==m.n[, ncol(m.n)])){
m.n <- as.data.table(m.n)
m.n[, recnum:=dsa$recnum]
setnames(m.n, colnames(m.n)[-length(colnames(m.n))], c("va", "v1", "v3", "v5", "v96", "v97", "v98", "v99", "v4", "v2", "allqs"))
setcolorder(m.n, c("recnum", colnames(m.n)[-length(colnames(m.n))]))
return(m.n)
}else{
print("not all rowsums equal!")
}
}

View File

@ -0,0 +1,47 @@
#uporabnost <- function(params){
#setwd("path od mape, kjer se nahaja ta glavna datoteka, npr. C:/mapa")
# Import data.table & functions ------------------------------------------------------
require("data.table")
source("modules/mod_uporabnost/R/gen.survey.str.R")
source("modules/mod_uporabnost/R/gen.usability.matrix.R")
source("modules/mod_uporabnost/R/calc.usability.R")
# Input data ------------------------------------------------------
params <- commandArgs(trailingOnly = TRUE)
ID <- params[1]
#get & import dsa: the main survey data file (containing only recnum, status, lurker and all variables relating to answers to survey questions)
dsa.file <- paste0("modules/mod_uporabnost/temp/data_", ID, ".csv")
dsa <- fread(dsa.file, header=T, drop=c(1:5, 7, 8))
#get question and item files
questions.file <- paste0("modules/mod_uporabnost/temp/questions_", ID, ".csv")
items.file <- paste0("modules/mod_uporabnost/temp/items_", ID, ".csv")
# Main & Output ------------------------------------------------------
#generate survey structure
survey.str <- gen.survey.str(colnames(dsa)[-(1)], questions.file, items.file)
if(any(!(is.data.table(survey.str)), nrow(survey.str)==0)){
write(survey.str, paste0("modules/mod_uporabnost/results/usability_", ID, ".csv"))
}else{
#delete invisible variables and types: 5, 9, 22, 23, 25
survey.str <- survey.str[visible==1 & !(tip %in% c(5, 9, 22, 23, 25)),]
#generate usability matrix
m.all <- gen.usability.matrix(dsa, survey.str)
if(any(!(is.data.table(m.all)), nrow(m.all)==0)){
write(m.all, paste0("modules/mod_uporabnost/results/usability_", ID, ".csv"))
}else{
#calculate usability indexes
m.final <- calc.usability(m.all, 3)
#write to results
write.csv2(m.final, paste0("modules/mod_uporabnost/results/usability_", ID, ".csv"), row.names = FALSE)
}
}
#}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -0,0 +1,2 @@
*
!.gitignore

20
composer.lock generated
View File

@ -308,16 +308,16 @@
}, },
{ {
"name": "guzzlehttp/psr7", "name": "guzzlehttp/psr7",
"version": "1.8.1", "version": "1.8.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/guzzle/psr7.git", "url": "https://github.com/guzzle/psr7.git",
"reference": "35ea11d335fd638b5882ff1725228b3d35496ab1" "reference": "dc960a912984efb74d0a90222870c72c87f10c91"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/35ea11d335fd638b5882ff1725228b3d35496ab1", "url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91",
"reference": "35ea11d335fd638b5882ff1725228b3d35496ab1", "reference": "dc960a912984efb74d0a90222870c72c87f10c91",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -375,7 +375,7 @@
"uri", "uri",
"url" "url"
], ],
"time": "2021-03-21 16:25:00" "time": "2021-04-26 09:17:50"
}, },
{ {
"name": "maxmind-db/reader", "name": "maxmind-db/reader",
@ -757,16 +757,16 @@
}, },
{ {
"name": "phpmailer/phpmailer", "name": "phpmailer/phpmailer",
"version": "v6.4.0", "version": "v6.4.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git", "url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "050d430203105c27c30efd1dce7aa421ad882d01" "reference": "9256f12d8fb0cd0500f93b19e18c356906cbed3d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/050d430203105c27c30efd1dce7aa421ad882d01", "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/9256f12d8fb0cd0500f93b19e18c356906cbed3d",
"reference": "050d430203105c27c30efd1dce7aa421ad882d01", "reference": "9256f12d8fb0cd0500f93b19e18c356906cbed3d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -819,7 +819,7 @@
} }
], ],
"description": "PHPMailer is a full-featured email creation and transfer class for PHP", "description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"time": "2021-03-31 20:06:42" "time": "2021-04-29 12:25:04"
}, },
{ {
"name": "psr/http-message", "name": "psr/http-message",

View File

@ -6355,6 +6355,7 @@ $lang = array (
'srv_off' => 'Izklopi', 'srv_off' => 'Izklopi',
'srv_para_neodgovori' => 'Neodgovor spremenljivke', 'srv_para_neodgovori' => 'Neodgovor spremenljivke',
'srv_usable_respondents' => 'Uporabni respondenti', 'srv_usable_respondents' => 'Uporabni respondenti',
'srv_kakovost' => 'Kakovost respondentov',
'srv_speeder_index' => 'Indeks hitrosti', 'srv_speeder_index' => 'Indeks hitrosti',
'srv_speeder_index_text' => 'V tabeli so označene enote, ki so anketo izpolnjevale prehitro (status 1); za vsako stran so prikazani časi respondentov, merjeni v sekundah. Uporabljena je metoda speeder index (Rossmann 2010). Pri označenih enotah obstaja določeno tveganje, da imajo slabšo kakovost (strategija zadostovanja). Priporočljivo je, da se odgovore označenih enot podrobneje analizira. <a href="https://www.1ka.si/d/sl/pomoc/vodic-za-uporabnike/status/indeks-hitrosti?from1ka=1" target="_blank">Več >></a>', 'srv_speeder_index_text' => 'V tabeli so označene enote, ki so anketo izpolnjevale prehitro (status 1); za vsako stran so prikazani časi respondentov, merjeni v sekundah. Uporabljena je metoda speeder index (Rossmann 2010). Pri označenih enotah obstaja določeno tveganje, da imajo slabšo kakovost (strategija zadostovanja). Priporočljivo je, da se odgovore označenih enot podrobneje analizira. <a href="https://www.1ka.si/d/sl/pomoc/vodic-za-uporabnike/status/indeks-hitrosti?from1ka=1" target="_blank">Več >></a>',
'srv_speeder_index_legend_0' => '0 - ni prehiter', 'srv_speeder_index_legend_0' => '0 - ni prehiter',

View File

@ -6240,6 +6240,7 @@ $lang = array (
'srv_off' => 'Turn off', 'srv_off' => 'Turn off',
'srv_para_neodgovori' => 'Item nonresponse', 'srv_para_neodgovori' => 'Item nonresponse',
'srv_usable_respondents' => 'Usable respondents', 'srv_usable_respondents' => 'Usable respondents',
'srv_kakovost' => 'Respondents quality',
'srv_speeder_index' => 'Speeder index', 'srv_speeder_index' => 'Speeder index',
'srv_speeder_index_text' => 'The table indicates the units that completed the survey too quickly (status 1); each page displays respondent\'s times measured in seconds. The speeder index method was used (Rossmann 2010). Labelled units have a certain risk of poor quality (satisficing strategy). It is recommended that the responses of the marked units are analysed in more detail. <a href="https://www.1ka.si/d/en/help/user-guide/dashboard/speed-index?from1ka=1" target="_blank">More >></a>', 'srv_speeder_index_text' => 'The table indicates the units that completed the survey too quickly (status 1); each page displays respondent\'s times measured in seconds. The speeder index method was used (Rossmann 2010). Labelled units have a certain risk of poor quality (satisficing strategy). It is recommended that the responses of the marked units are analysed in more detail. <a href="https://www.1ka.si/d/en/help/user-guide/dashboard/speed-index?from1ka=1" target="_blank">More >></a>',
'srv_speeder_index_legend_0' => '0 - not too fast', 'srv_speeder_index_legend_0' => '0 - not too fast',

View File

@ -950,6 +950,7 @@ return array(
'SurveyInvitationsNew' => $baseDir . '/admin/survey/classes/surveyEmails/class.SurveyInvitationsNew.php', 'SurveyInvitationsNew' => $baseDir . '/admin/survey/classes/surveyEmails/class.SurveyInvitationsNew.php',
'SurveyInvitationsSqualo' => $baseDir . '/admin/survey/modules/mod_squalo/class.SurveyInvitationsSqualo.php', 'SurveyInvitationsSqualo' => $baseDir . '/admin/survey/modules/mod_squalo/class.SurveyInvitationsSqualo.php',
'SurveyJsonSurveyData' => $baseDir . '/admin/survey/modules/mod_json_survey_export/class.SurveyJsonSurveyData.php', 'SurveyJsonSurveyData' => $baseDir . '/admin/survey/modules/mod_json_survey_export/class.SurveyJsonSurveyData.php',
'SurveyKakovost' => $baseDir . '/admin/survey/modules/mod_kakovost/class.SurveyKakovost.php',
'SurveyLanguageTechnology' => $baseDir . '/admin/survey/classes/class.SurveyLanguageTechnology.php', 'SurveyLanguageTechnology' => $baseDir . '/admin/survey/classes/class.SurveyLanguageTechnology.php',
'SurveyList' => $baseDir . '/admin/survey/classes/class.SurveyList.php', 'SurveyList' => $baseDir . '/admin/survey/classes/class.SurveyList.php',
'SurveyLog' => $baseDir . '/admin/survey/classes/log/class.SurveyLog.php', 'SurveyLog' => $baseDir . '/admin/survey/classes/log/class.SurveyLog.php',

View File

@ -11,8 +11,8 @@ return array(
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php', 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
'3109cb1a231dcd04bee1f9f620d46975' => $vendorDir . '/paragonie/sodium_compat/autoload.php', '3109cb1a231dcd04bee1f9f620d46975' => $vendorDir . '/paragonie/sodium_compat/autoload.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
'2bf0564df058fbe21b5554296cc8571a' => $baseDir . '/main/survey/class.EvalvacijaMain.php', '2bf0564df058fbe21b5554296cc8571a' => $baseDir . '/main/survey/class.EvalvacijaMain.php',
'5bc35216aa4f6cb823ce4a5cec52fdce' => $baseDir . '/main/survey/mobile-detect/Mobile_Detect.php', '5bc35216aa4f6cb823ce4a5cec52fdce' => $baseDir . '/main/survey/mobile-detect/Mobile_Detect.php',

View File

@ -12,8 +12,8 @@ class ComposerStaticInit6b03163c371c5541881b55b762b8c779
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php', 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
'3109cb1a231dcd04bee1f9f620d46975' => __DIR__ . '/..' . '/paragonie/sodium_compat/autoload.php', '3109cb1a231dcd04bee1f9f620d46975' => __DIR__ . '/..' . '/paragonie/sodium_compat/autoload.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
'2bf0564df058fbe21b5554296cc8571a' => __DIR__ . '/../..' . '/main/survey/class.EvalvacijaMain.php', '2bf0564df058fbe21b5554296cc8571a' => __DIR__ . '/../..' . '/main/survey/class.EvalvacijaMain.php',
'5bc35216aa4f6cb823ce4a5cec52fdce' => __DIR__ . '/../..' . '/main/survey/mobile-detect/Mobile_Detect.php', '5bc35216aa4f6cb823ce4a5cec52fdce' => __DIR__ . '/../..' . '/main/survey/mobile-detect/Mobile_Detect.php',
@ -1154,6 +1154,7 @@ class ComposerStaticInit6b03163c371c5541881b55b762b8c779
'SurveyInvitationsNew' => __DIR__ . '/../..' . '/admin/survey/classes/surveyEmails/class.SurveyInvitationsNew.php', 'SurveyInvitationsNew' => __DIR__ . '/../..' . '/admin/survey/classes/surveyEmails/class.SurveyInvitationsNew.php',
'SurveyInvitationsSqualo' => __DIR__ . '/../..' . '/admin/survey/modules/mod_squalo/class.SurveyInvitationsSqualo.php', 'SurveyInvitationsSqualo' => __DIR__ . '/../..' . '/admin/survey/modules/mod_squalo/class.SurveyInvitationsSqualo.php',
'SurveyJsonSurveyData' => __DIR__ . '/../..' . '/admin/survey/modules/mod_json_survey_export/class.SurveyJsonSurveyData.php', 'SurveyJsonSurveyData' => __DIR__ . '/../..' . '/admin/survey/modules/mod_json_survey_export/class.SurveyJsonSurveyData.php',
'SurveyKakovost' => __DIR__ . '/../..' . '/admin/survey/modules/mod_kakovost/class.SurveyKakovost.php',
'SurveyLanguageTechnology' => __DIR__ . '/../..' . '/admin/survey/classes/class.SurveyLanguageTechnology.php', 'SurveyLanguageTechnology' => __DIR__ . '/../..' . '/admin/survey/classes/class.SurveyLanguageTechnology.php',
'SurveyList' => __DIR__ . '/../..' . '/admin/survey/classes/class.SurveyList.php', 'SurveyList' => __DIR__ . '/../..' . '/admin/survey/classes/class.SurveyList.php',
'SurveyLog' => __DIR__ . '/../..' . '/admin/survey/classes/log/class.SurveyLog.php', 'SurveyLog' => __DIR__ . '/../..' . '/admin/survey/classes/log/class.SurveyLog.php',

View File

@ -1615,147 +1615,6 @@
"promise" "promise"
] ]
}, },
{
"name": "phpmailer/phpmailer",
"version": "v6.4.0",
"version_normalized": "6.4.0.0",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "050d430203105c27c30efd1dce7aa421ad882d01"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/050d430203105c27c30efd1dce7aa421ad882d01",
"reference": "050d430203105c27c30efd1dce7aa421ad882d01",
"shasum": ""
},
"require": {
"ext-ctype": "*",
"ext-filter": "*",
"ext-hash": "*",
"php": ">=5.5.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
"doctrine/annotations": "^1.2",
"phpcompatibility/php-compatibility": "^9.3.5",
"roave/security-advisories": "dev-latest",
"squizlabs/php_codesniffer": "^3.5.6",
"yoast/phpunit-polyfills": "^0.2.0"
},
"suggest": {
"ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses",
"hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
"league/oauth2-google": "Needed for Google XOAUTH2 authentication",
"psr/log": "For optional PSR-3 debug logging",
"stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication",
"symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)"
},
"time": "2021-03-31 20:06:42",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"PHPMailer\\PHPMailer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-only"
],
"authors": [
{
"name": "Marcus Bointon",
"email": "phpmailer@synchromedia.co.uk"
},
{
"name": "Jim Jagielski",
"email": "jimjag@gmail.com"
},
{
"name": "Andy Prevost",
"email": "codeworxtech@users.sourceforge.net"
},
{
"name": "Brent R. Matzelle"
}
],
"description": "PHPMailer is a full-featured email creation and transfer class for PHP"
},
{
"name": "guzzlehttp/psr7",
"version": "1.8.1",
"version_normalized": "1.8.1.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "35ea11d335fd638b5882ff1725228b3d35496ab1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/35ea11d335fd638b5882ff1725228b3d35496ab1",
"reference": "35ea11d335fd638b5882ff1725228b3d35496ab1",
"shasum": ""
},
"require": {
"php": ">=5.4.0",
"psr/http-message": "~1.0",
"ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"ext-zlib": "*",
"phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10"
},
"suggest": {
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
},
"time": "2021-03-21 16:25:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.7-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "Tobias Schultze",
"homepage": "https://github.com/Tobion"
}
],
"description": "PSR-7 message implementation that also provides common utility methods",
"keywords": [
"http",
"message",
"psr-7",
"request",
"response",
"stream",
"uri",
"url"
]
},
{ {
"name": "stripe/stripe-php", "name": "stripe/stripe-php",
"version": "v7.77.0", "version": "v7.77.0",
@ -1961,5 +1820,146 @@
"secret-key cryptography", "secret-key cryptography",
"side-channel resistant" "side-channel resistant"
] ]
},
{
"name": "phpmailer/phpmailer",
"version": "v6.4.1",
"version_normalized": "6.4.1.0",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "9256f12d8fb0cd0500f93b19e18c356906cbed3d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/9256f12d8fb0cd0500f93b19e18c356906cbed3d",
"reference": "9256f12d8fb0cd0500f93b19e18c356906cbed3d",
"shasum": ""
},
"require": {
"ext-ctype": "*",
"ext-filter": "*",
"ext-hash": "*",
"php": ">=5.5.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
"doctrine/annotations": "^1.2",
"phpcompatibility/php-compatibility": "^9.3.5",
"roave/security-advisories": "dev-latest",
"squizlabs/php_codesniffer": "^3.5.6",
"yoast/phpunit-polyfills": "^0.2.0"
},
"suggest": {
"ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses",
"hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
"league/oauth2-google": "Needed for Google XOAUTH2 authentication",
"psr/log": "For optional PSR-3 debug logging",
"stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication",
"symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)"
},
"time": "2021-04-29 12:25:04",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"PHPMailer\\PHPMailer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-only"
],
"authors": [
{
"name": "Marcus Bointon",
"email": "phpmailer@synchromedia.co.uk"
},
{
"name": "Jim Jagielski",
"email": "jimjag@gmail.com"
},
{
"name": "Andy Prevost",
"email": "codeworxtech@users.sourceforge.net"
},
{
"name": "Brent R. Matzelle"
}
],
"description": "PHPMailer is a full-featured email creation and transfer class for PHP"
},
{
"name": "guzzlehttp/psr7",
"version": "1.8.2",
"version_normalized": "1.8.2.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "dc960a912984efb74d0a90222870c72c87f10c91"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91",
"reference": "dc960a912984efb74d0a90222870c72c87f10c91",
"shasum": ""
},
"require": {
"php": ">=5.4.0",
"psr/http-message": "~1.0",
"ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"ext-zlib": "*",
"phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10"
},
"suggest": {
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
},
"time": "2021-04-26 09:17:50",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.7-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "Tobias Schultze",
"homepage": "https://github.com/Tobion"
}
],
"description": "PSR-7 message implementation that also provides common utility methods",
"keywords": [
"http",
"message",
"psr-7",
"request",
"response",
"stream",
"uri",
"url"
]
} }
] ]

View File

@ -0,0 +1,29 @@
name: Static analysis
on:
pull_request:
jobs:
php-cs-fixer:
name: PHP-CS-Fixer
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
coverage: none
extensions: mbstring
- name: Download dependencies
run: composer update --no-interaction --no-progress
- name: Download PHP CS Fixer
run: composer require "friendsofphp/php-cs-fixer:2.18.4"
- name: Execute PHP CS Fixer
run: vendor/bin/php-cs-fixer fix --diff-format udiff --dry-run

View File

@ -7,16 +7,22 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [Unreleased] ## Unreleased
## [1.8.1] - 2021-03-21 ## 1.8.2 - 2021-04-26
### Fixed
- Handle possibly unset `url` in `stream_get_meta_data`
## 1.8.1 - 2021-03-21
### Fixed ### Fixed
- Issue parsing IPv6 URLs - Issue parsing IPv6 URLs
- Issue modifying ServerRequest lost all its attributes - Issue modifying ServerRequest lost all its attributes
## [1.8.0] - 2021-03-21 ## 1.8.0 - 2021-03-21
### Added ### Added
@ -28,7 +34,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Issue when creating stream from `php://input` and curl-ext is not installed - Issue when creating stream from `php://input` and curl-ext is not installed
- Broken `Utils::tryFopen()` on PHP 8 - Broken `Utils::tryFopen()` on PHP 8
## [1.7.0] - 2020-09-30 ## 1.7.0 - 2020-09-30
### Added ### Added
@ -272,7 +278,6 @@ Currently unsupported:
[Unreleased]: https://github.com/guzzle/psr7/compare/1.6.0...HEAD
[1.6.0]: https://github.com/guzzle/psr7/compare/1.5.2...1.6.0 [1.6.0]: https://github.com/guzzle/psr7/compare/1.5.2...1.6.0
[1.5.2]: https://github.com/guzzle/psr7/compare/1.5.1...1.5.2 [1.5.2]: https://github.com/guzzle/psr7/compare/1.5.1...1.5.2
[1.5.1]: https://github.com/guzzle/psr7/compare/1.5.0...1.5.1 [1.5.1]: https://github.com/guzzle/psr7/compare/1.5.0...1.5.1

View File

@ -109,7 +109,7 @@ class Uri implements UriInterface
$url $url
); );
$result = parse_url($prefix.$encodedUrl); $result = parse_url($prefix . $encodedUrl);
if ($result === false) { if ($result === false) {
return false; return false;

View File

@ -317,7 +317,8 @@ final class Utils
* The 'php://input' is a special stream with quirks and inconsistencies. * The 'php://input' is a special stream with quirks and inconsistencies.
* We avoid using that stream by reading it into php://temp * We avoid using that stream by reading it into php://temp
*/ */
if (\stream_get_meta_data($resource)['uri'] === 'php://input') { $metaData = \stream_get_meta_data($resource);
if (isset($metaData['uri']) && $metaData['uri'] === 'php://input') {
$stream = self::tryFopen('php://temp', 'w+'); $stream = self::tryFopen('php://temp', 'w+');
fwrite($stream, stream_get_contents($resource)); fwrite($stream, stream_get_contents($resource));
fseek($stream, 0); fseek($stream, 0);

View File

@ -2,6 +2,8 @@
Please disclose any security issues or vulnerabilities found through [Tidelift's coordinated disclosure system](https://tidelift.com/security) or to the maintainers privately. Please disclose any security issues or vulnerabilities found through [Tidelift's coordinated disclosure system](https://tidelift.com/security) or to the maintainers privately.
PHPMailer versions between 6.1.8 and 6.4.0 contain a regression of the earlier CVE-2018-19296 object injection vulnerability as a result of [a fix for Windows UNC paths in 6.1.8](https://github.com/PHPMailer/PHPMailer/commit/e2e07a355ee8ff36aba21d0242c5950c56e4c6f9). Recorded as [CVE-2020-36326](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2020-36326). Reported by Fariskhi Vidyan via Tidelift. 6.4.1 fixes this issue, and also enforces stricter checks for URL schemes in local path contexts.
PHPMailer versions 6.1.5 and earlier contain an output escaping bug that occurs in `Content-Type` and `Content-Disposition` when filenames passed into `addAttachment` and other methods that accept attachment names contain double quote characters, in contravention of RFC822 3.4.1. No specific vulnerability has been found relating to this, but it could allow file attachments to bypass attachment filters that are based on matching filename extensions. Recorded as [CVE-2020-13625](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2020-13625). Reported by Elar Lang of Clarified Security. PHPMailer versions 6.1.5 and earlier contain an output escaping bug that occurs in `Content-Type` and `Content-Disposition` when filenames passed into `addAttachment` and other methods that accept attachment names contain double quote characters, in contravention of RFC822 3.4.1. No specific vulnerability has been found relating to this, but it could allow file attachments to bypass attachment filters that are based on matching filename extensions. Recorded as [CVE-2020-13625](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2020-13625). Reported by Elar Lang of Clarified Security.
PHPMailer versions prior to 6.0.6 and 5.2.27 are vulnerable to an object injection attack by passing `phar://` paths into `addAttachment()` and other functions that may receive unfiltered local paths, possibly leading to RCE. Recorded as [CVE-2018-19296](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2018-19296). See [this article](https://knasmueller.net/5-answers-about-php-phar-exploitation) for more info on this type of vulnerability. Mitigated by blocking the use of paths containing URL-protocol style prefixes such as `phar://`. Reported by Sehun Oh of cyberone.kr. PHPMailer versions prior to 6.0.6 and 5.2.27 are vulnerable to an object injection attack by passing `phar://` paths into `addAttachment()` and other functions that may receive unfiltered local paths, possibly leading to RCE. Recorded as [CVE-2018-19296](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2018-19296). See [this article](https://knasmueller.net/5-answers-about-php-phar-exploitation) for more info on this type of vulnerability. Mitigated by blocking the use of paths containing URL-protocol style prefixes such as `phar://`. Reported by Sehun Oh of cyberone.kr.

View File

@ -1 +1 @@
6.4.0 6.4.1

View File

@ -57,5 +57,9 @@
"PHPMailer\\Test\\": "test/" "PHPMailer\\Test\\": "test/"
} }
}, },
"license": "LGPL-2.1-only" "license": "LGPL-2.1-only",
"scripts": {
"check": "./vendor/bin/phpcs",
"test": "./vendor/bin/phpunit"
}
} }

View File

@ -16,11 +16,11 @@ $PHPMAILER_LANG['file_access'] = 'Немає доступу до фай
$PHPMAILER_LANG['file_open'] = 'Помилка файлової системи: не вдається відкрити файл: '; $PHPMAILER_LANG['file_open'] = 'Помилка файлової системи: не вдається відкрити файл: ';
$PHPMAILER_LANG['from_failed'] = 'Невірна адреса відправника: '; $PHPMAILER_LANG['from_failed'] = 'Невірна адреса відправника: ';
$PHPMAILER_LANG['instantiate'] = 'Неможливо запустити функцію mail().'; $PHPMAILER_LANG['instantiate'] = 'Неможливо запустити функцію mail().';
$PHPMAILER_LANG['provide_address'] = 'Будь-ласка, введіть хоча б одну email-адресу отримувача.'; $PHPMAILER_LANG['provide_address'] = 'Будь ласка, введіть хоча б одну email-адресу отримувача.';
$PHPMAILER_LANG['mailer_not_supported'] = ' - поштовий сервер не підтримується.'; $PHPMAILER_LANG['mailer_not_supported'] = ' - поштовий сервер не підтримується.';
$PHPMAILER_LANG['recipients_failed'] = 'Помилка SMTP: не вдалося відправлення для таких отримувачів: '; $PHPMAILER_LANG['recipients_failed'] = 'Помилка SMTP: не вдалося відправлення для таких отримувачів: ';
$PHPMAILER_LANG['empty_message'] = 'Пусте повідомлення'; $PHPMAILER_LANG['empty_message'] = 'Пусте повідомлення';
$PHPMAILER_LANG['invalid_address'] = 'Не відправлено через невірний формат email-адреси: '; $PHPMAILER_LANG['invalid_address'] = 'Не відправлено через неправильний формат email-адреси: ';
$PHPMAILER_LANG['signing'] = 'Помилка підпису: '; $PHPMAILER_LANG['signing'] = 'Помилка підпису: ';
$PHPMAILER_LANG['smtp_connect_failed'] = 'Помилка з\'єднання з SMTP-сервером'; $PHPMAILER_LANG['smtp_connect_failed'] = 'Помилка з\'єднання з SMTP-сервером';
$PHPMAILER_LANG['smtp_error'] = 'Помилка SMTP-сервера: '; $PHPMAILER_LANG['smtp_error'] = 'Помилка SMTP-сервера: ';

View File

@ -748,7 +748,7 @@ class PHPMailer
* *
* @var string * @var string
*/ */
const VERSION = '6.4.0'; const VERSION = '6.4.1';
/** /**
* Error severity: message only, continue processing. * Error severity: message only, continue processing.
@ -1721,9 +1721,10 @@ class PHPMailer
fwrite($mail, $header); fwrite($mail, $header);
fwrite($mail, $body); fwrite($mail, $body);
$result = pclose($mail); $result = pclose($mail);
$addrinfo = static::parseAddresses($toAddr);
$this->doCallback( $this->doCallback(
($result === 0), ($result === 0),
[$toAddr], [[$addrinfo['address'], $addrinfo['name']]],
$this->cc, $this->cc,
$this->bcc, $this->bcc,
$this->Subject, $this->Subject,
@ -1810,7 +1811,8 @@ class PHPMailer
*/ */
protected static function isPermittedPath($path) protected static function isPermittedPath($path)
{ {
return !preg_match('#^[a-z]+://#i', $path); //Matches scheme definition from https://tools.ietf.org/html/rfc3986#section-3.1
return !preg_match('#^[a-z][a-z\d+.-]*://#i', $path);
} }
/** /**
@ -1822,12 +1824,15 @@ class PHPMailer
*/ */
protected static function fileIsAccessible($path) protected static function fileIsAccessible($path)
{ {
if (!static::isPermittedPath($path)) {
return false;
}
$readable = file_exists($path); $readable = file_exists($path);
//If not a UNC path (expected to start with \\), check read permission, see #2069 //If not a UNC path (expected to start with \\), check read permission, see #2069
if (strpos($path, '\\\\') !== 0) { if (strpos($path, '\\\\') !== 0) {
$readable = $readable && is_readable($path); $readable = $readable && is_readable($path);
} }
return static::isPermittedPath($path) && $readable; return $readable;
} }
/** /**
@ -1876,7 +1881,17 @@ class PHPMailer
if ($this->SingleTo && count($toArr) > 1) { if ($this->SingleTo && count($toArr) > 1) {
foreach ($toArr as $toAddr) { foreach ($toArr as $toAddr) {
$result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params); $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
$this->doCallback($result, [$toAddr], $this->cc, $this->bcc, $this->Subject, $body, $this->From, []); $addrinfo = static::parseAddresses($toAddr);
$this->doCallback(
$result,
[[$addrinfo['address'], $addrinfo['name']]],
$this->cc,
$this->bcc,
$this->Subject,
$body,
$this->From,
[]
);
} }
} else { } else {
$result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
@ -1965,7 +1980,7 @@ class PHPMailer
$isSent = true; $isSent = true;
} }
$callbacks[] = ['issent' => $isSent, 'to' => $to[0]]; $callbacks[] = ['issent' => $isSent, 'to' => $to[0], 'name' => $to[1]];
} }
} }
@ -1986,7 +2001,7 @@ class PHPMailer
foreach ($callbacks as $cb) { foreach ($callbacks as $cb) {
$this->doCallback( $this->doCallback(
$cb['issent'], $cb['issent'],
[$cb['to']], [[$cb['to'], $cb['name']]],
[], [],
[], [],
$this->Subject, $this->Subject,

View File

@ -46,7 +46,7 @@ class POP3
* *
* @var string * @var string
*/ */
const VERSION = '6.4.0'; const VERSION = '6.4.1';
/** /**
* Default POP3 port number. * Default POP3 port number.

View File

@ -35,7 +35,7 @@ class SMTP
* *
* @var string * @var string
*/ */
const VERSION = '6.4.0'; const VERSION = '6.4.1';
/** /**
* SMTP line break constant. * SMTP line break constant.