diff --git a/admin/survey/SurveyAdmin.php b/admin/survey/SurveyAdmin.php index b99317170..76349cefc 100644 --- a/admin/survey/SurveyAdmin.php +++ b/admin/survey/SurveyAdmin.php @@ -1872,6 +1872,16 @@ class SurveyAdmin echo ''; echo '
  • '; + # kakovost resp - V DELU - ZAENKRAT SAMO ADMINI + if ($admin_type === '0') { + echo '
  • '; + echo ''; + echo $lang['srv_kakovost'] . ''; + echo '
  • '; + echo '
  • '; + } + # speeder index - V DELU - ZAENKRAT SAMO ADMINI if ($admin_type === '0') { echo '
  • '; @@ -2336,6 +2346,13 @@ class SurveyAdmin $SUR->displayUporabnost(); echo ' '; } + // prikaze modul kakovost + elseif ($_GET['a'] == A_KAKOVOST_RESP) { + echo '
    '; + $SUR = new SurveyKakovost($this->anketa); + $SUR->displayKakovost(); + echo '
    '; + } // Prikaze analizo hitrosti respondenta elseif ($_GET['a'] == A_SPEEDER_INDEX) { echo '
    '; @@ -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); } else if ($navigation == 1) { diff --git a/admin/survey/classes/tracking/CrossRoad.php b/admin/survey/classes/tracking/CrossRoad.php index 6dcd8f336..2b584acde 100644 --- a/admin/survey/classes/tracking/CrossRoad.php +++ b/admin/survey/classes/tracking/CrossRoad.php @@ -35,6 +35,7 @@ class CrossRoad { break; case A_NONRESPONSE_GRAPH: case A_USABLE_RESP: + case A_KAKOVOST_RESP: case A_SPEEDER_INDEX: case A_TEXT_ANALYSIS: case A_GEOIP_LOCATION: diff --git a/admin/survey/definition.php b/admin/survey/definition.php index d1e68e676..3a9176331 100644 --- a/admin/survey/definition.php +++ b/admin/survey/definition.php @@ -57,6 +57,7 @@ define("A_NONRESPONSE_GRAPH", "nonresponse_graph"); define("A_PARA_GRAPH", "para_graph"); define("A_USABLE_RESP", "usable_resp"); + define("A_KAKOVOST_RESP", "kakovost_resp"); define("A_SPEEDER_INDEX", "speeder_index"); define("A_TEXT_ANALYSIS", "text_analysis"); define("A_GEOIP_LOCATION", "geoip_location"); diff --git a/admin/survey/modules/mod_kakovost/R/calc.usability.R b/admin/survey/modules/mod_kakovost/R/calc.usability.R new file mode 100644 index 000000000..a464c3024 --- /dev/null +++ b/admin/survey/modules/mod_kakovost/R/calc.usability.R @@ -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) + } + } +} \ No newline at end of file diff --git a/admin/survey/modules/mod_kakovost/R/gen.survey.str.R b/admin/survey/modules/mod_kakovost/R/gen.survey.str.R new file mode 100644 index 000000000..0d523b177 --- /dev/null +++ b/admin/survey/modules/mod_kakovost/R/gen.survey.str.R @@ -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])) + } + } + } +} diff --git a/admin/survey/modules/mod_kakovost/R/gen.usability.matrix.R b/admin/survey/modules/mod_kakovost/R/gen.usability.matrix.R new file mode 100644 index 000000000..2f0409a9e --- /dev/null +++ b/admin/survey/modules/mod_kakovost/R/gen.usability.matrix.R @@ -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!") + } +} \ No newline at end of file diff --git a/admin/survey/modules/mod_kakovost/R/kakovost.R b/admin/survey/modules/mod_kakovost/R/kakovost.R new file mode 100644 index 000000000..cd4f932c9 --- /dev/null +++ b/admin/survey/modules/mod_kakovost/R/kakovost.R @@ -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) + } +} + +#} \ No newline at end of file diff --git a/admin/survey/modules/mod_kakovost/class.SurveyKakovost.php b/admin/survey/modules/mod_kakovost/class.SurveyKakovost.php new file mode 100644 index 000000000..300583433 --- /dev/null +++ b/admin/survey/modules/mod_kakovost/class.SurveyKakovost.php @@ -0,0 +1,1023 @@ +ul evealvacija + if ((int)$anketa > 0){ + + $this->anketa = $anketa; + + # polovimo vrsto tabel (aktivne / neaktivne) + SurveyInfo :: getInstance()->SurveyInit($this->anketa); + if (SurveyInfo::getInstance()->getSurveyColumn('db_table') == 1) { + $this->db_table = '_active'; + } + + SurveyAnalysisHelper::getInstance()->Init($this->anketa); + + $this->_CURRENT_STATUS_FILTER = STATUS_FIELD.' ~ /6|5/'; + + Common::deletePreviewData($this->anketa); + + // Poskrbimo za datoteko s podatki + $SDF = SurveyDataFile::get_instance(); + $SDF->init($this->anketa); + + if($generateDataFile) + $SDF->prepareFiles(); + + $this->headFileName = $SDF->getHeaderFileName(); + $this->dataFileName = $SDF->getDataFileName(); + $this->dataFileStatus = $SDF->getStatus(); + + if ( $this->dataFileStatus == FILE_STATUS_NO_DATA || $this->dataFileStatus == FILE_STATUS_SRV_DELETED) { + Common::noDataAlert(); + exit(); + } + + # Inicializiramo in polovimo nastavitve missing profila + SurveyStatusProfiles::Init($this->anketa); + SurveyUserSetting::getInstance()->Init($this->anketa, $global_user_id); + SurveyConditionProfiles :: Init($this->anketa, $global_user_id); + SurveyTimeProfiles :: Init($this->anketa, $global_user_id); + SurveyVariablesProfiles :: Init($this->anketa, $global_user_id); + SurveyDataSettingProfiles :: Init($this->anketa); + + + // preberemo nastavitve iz baze (prej v sessionu) + SurveyUserSession::Init($this->anketa); + $this->sessionData = SurveyUserSession::getData(); + + if(isset($_SESSION['sid_'.$this->anketa]['usabilityIcons_settings'])) + $this->displayEditIconsSettings = ($_SESSION['sid_'.$this->anketa]['usabilityIcons_settings']); + + if (file_exists($this->headFileName) && $this->headFileName !== null && $this->headFileName != ''){ + $this->_HEADERS = unserialize(file_get_contents($this->headFileName)); + } + + # nastavimo vse filtre + $this->setUpFilter(); + + # nastavimo filtre uporabnika + $this->setUserFilters(); + + # nastavimo sortiranje + if(isset($_GET['sortField'])) + $this->sortField = $_GET['sortField']; + if(isset($_GET['sortType'])) + $this->sortType = $_GET['sortType']; + } + } + + + // Prikažemo stran + public function displayKakovost(){ + global $lang; + + // Prikaz nastavitev + $this->displayKakovostSettings(); + + // Izvedemo pripravo datoteke + $this->prepareData(); + + // Napolnimo podatke v array + $this->fillData(); + + // Izrisemo tabelo + $this->displayKakovostTable(); + + // Na koncu pobrisemo zacasne datoteke + //$this->deleteTemp(); + } + + // Prikazemo tabelo + private function displayKakovostTable(){ + global $site_path; + global $lang; + global $admin_type; + + echo '
    '; + + echo ''; + + + // NASLOVNE VRSTICE + if($this->sortType == 1){ + $sortType = 0; + $arrow = ' '; + } + else{ + $sortType = 1; + $arrow = ' '; + } + + if($admin_type == '0' || $admin_type == '1') + echo ''; + + echo ''; + echo ''; + + echo ''; + + echo ''; + + echo ''; + + // ali odstranimo vse stolpce s podrobnimi vrednostmi (-1, -2...) + if ($this->show_details == true) { + foreach ($this->_missings AS $value => $text){ + $cnt_miss++; + echo ""; + } + foreach ($this->_unsets AS $value => $text){ + $cnt_undefined++; + echo ""; + } + } + + // ali prikazemo podrobne izracune + if ($this->show_calculations == true) { + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + + echo ''; + + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + + echo ''; + echo ''; + echo ''; + + + // VRSTICE S PODATKI + foreach($this->usability['data'] as $user){ + + // Prva vrstica z vrednostmi + echo ''; + + if($admin_type == '0' || $admin_type == '1'){ + + $sql = sisplet_query("SELECT id FROM srv_user WHERE ank_id='".$this->anketa."' AND recnum='".$user['recnum']."'"); + $row = mysqli_fetch_array($sql); + + echo ''; + } + + + echo ''; + + // Vsi + echo ''; + + // Ustrezni + echo ''; + + // Non-substantive + echo ''; + + // Non-response + echo ''; + + // Skupaj + echo ''; + + // Breakoffs + echo ''; + + // Uporabni + echo ''; + echo ''; + + // ali odstranimo vse stolpce s podrobnimi vrednostmi (-1, -2...) + if ($this->show_details == true) { + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + + // ali prikazemo podrobne izracune + if ($this->show_calculations == true) { + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + + echo ''; + + + // Druga vrstica s procenti + echo ''; + + // Ustrezni + echo ''; + + // Non-substantive + echo ''; + + // Non-response + echo ''; + + // Skupaj + echo ''; + + // Breakoffs + echo ''; + + // Uporabni + echo ''; + + // ali odstranimo vse stolpce s podrobnimi vrednostmi (-1, -2...) + if ($this->show_details == true) { + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + + echo ''; + } + + + echo '
    User IDRecnum'./*$lang['recnum'].*/($this->sortField=='recnum' ? $arrow : '').''.$lang['srv_usableResp_qcount'].''.$lang['srv_usableResp_exposed'].''.$lang['srv_usableResp_breakoff'].($this->sortField=='breakoff' ? $arrow : '').''.$lang['srv_usableResp_usability'].'{$value}
    (".$lang['srv_usableResp_'.$text].")
    {$value}
    (".$lang['srv_usableResp_'.$text].")
    UNLUMLUCLUILUAQ
    '.$lang['srv_anl_valid'].($this->sortField=='valid' ? $arrow : '').''.$lang['srv_usableResp_nonsubstantive'].($this->sortField=='nonsubstantive' ? $arrow : '').''.$lang['srv_usableResp_nonresponse'].($this->sortField=='nonresponse' ? $arrow : '').''.$lang['srv_anl_suma1'].'%'.($this->sortField=='usable' ? $arrow : '').'Status'.($this->sortField=='status' ? $arrow : '').'
    '.$row['id'].''.$user['recnum'].''.$user['all'].''.$user['valid'].''.$user['nonsubstantive'].''.$user['nonresponse'].''.($user['valid']+$user['nonsubstantive']+$user['nonresponse']+$user['breakoff']).''.$user['breakoff'].''.$user['usable'].''.$user['status'].''.$user['-1'].''.$user['-2'].''.$user['-3'].''.$user['-4'].''.$user['-5'].''.$user['-97'].''.$user['-98'].''.$user['-99'].''.$user['UNL'].''.$user['UML'].''.$user['UCL'].''.$user['UIL'].''.$user['UAQ'].'
    '.$user['validPercent'].''.$user['nonsubstantivePercent'].''.$user['nonresponsePercent'].'100%'.$user['breakoffPercent'].''.$user['usablePercent'].''.$user['-1_percent'].''.$user['-2_percent'].''.$user['-3_percent'].''.$user['-4_percent'].''.$user['-5_percent'].''.$user['-97_percent'].''.$user['-98_percent'].''.$user['-99_percent'].'
    '; + + if($this->usability['all'] > 0){ + echo '
    '; + + //echo ''.$lang['srv_usableResp_usability'].': '; + echo ''.$lang['srv_usableResp_usable_unit'].' - Status 2 ('.$this->top_usable_limit.'%-100%): '.$this->usability['usable'].' ('.common::formatNumber($this->usability['usable']/$this->usability['all']*100, 0, null, '%').')'; + echo ''.$lang['srv_usableResp_partusable_unit'].' - Status 1 ('.$this->bottom_usable_limit.'%-'.$this->top_usable_limit.'%): '.$this->usability['partusable'].' ('.common::formatNumber($this->usability['partusable']/$this->usability['all']*100, 0, null, '%').')'; + echo ''.$lang['srv_usableResp_unusable_unit'].' - Status 0 (0%-'.$this->bottom_usable_limit.'%): '.$this->usability['unusable'].' ('.common::formatNumber($this->usability['unusable']/$this->usability['all']*100, 0, null, '%').')'; + + echo '
    '; + } + + echo '
    '; + } + + private function displayKakovostSettings(){ + global $lang; + + // Div z nastavitvami ki se razpre + echo '
    displayEditIconsSettings ? '' : ' style="display:none;"').'>'; + echo '
    '.$lang['srv_data_settings_checkboxes2'].'
    '; + + + echo '
    '; + + echo $lang['srv_usableResp_limit'].': '; + + echo ''.$lang['srv_usableResp_bottom_limit'].': %'; + echo ''.$lang['srv_usableResp_top_limit'].': %'; + + echo '
    '; + + echo '
    '; + echo $lang['srv_usableResp_show'].': '; + + // Prikaz neničelnih stolpcev + /*echo '';*/ + + // Prikaz podrobnosti + echo ''; + + // Prikaz podrobnih izracunov + echo ''; + echo '
    '; + + echo '
    '; + + + echo '
    '; + } + + + // Zgeneriramo pdf analizo + private function prepareData(){ + global $site_path; + global $lang; + global $admin_type; + + // Zgeneriramo zacasne csv datoteke + $this->prepareDataCSV(); + $this->prepareQuestionCSV(); + $this->prepareItemCSV(); + + // Poklicemo R skripto in zgeneriramo pdf + $script = $site_path . SCRIPT_FOLDER . '/kakovost.R'; + $out = exec('Rscript '.$script.' '.$this->anketa.' 2>&1', $output, $return_var); + + // Testiranje - izpis errorjev + if($admin_type == 0){ + echo '
    '; + echo 'Rscript '.$script; + //echo '
    '.$out.'
    '; + var_dump($output); + echo '
    '; + } + } + + // Napolnimo podatke v array + private function fillData(){ + global $site_path; + global $lang; + + $result_folder = $site_path . RESULTS_FOLDER.'/'; + + if (($handle = fopen($result_folder."usability_".$this->anketa.".csv", "r")) !== FALSE) { + + // Loop po vrsticah + $cnt = 0; + while (($row = fgetcsv($handle, 1000, ';')) !== FALSE) { + + if($cnt == 0) + $row = fgetcsv($handle, 1000, ';'); + + // Preberemo se drugo vrstico, ker so v parih + $row2 = fgetcsv($handle, 1000, ';'); + + + // Obarvamo vrstico glede na status (belo, rumeno, rdece) + if($row2[7] < (int)$this->bottom_usable_limit){ + $css_usable = 'unusable'; + $status = 0; + $this->usability['unusable']++; + } + elseif($row2[7] >= (int)$this->bottom_usable_limit && $row2[7] < (int)$this->top_usable_limit){ + $css_usable = 'partusable'; + $status = 1; + $this->usability['partusable']++; + } + else{ + $css_usable = 'usable'; + $status = 2; + $this->usability['usable']++; + } + $this->usability['all']++; + + + // Nastavimo izracunane podatke za respondenta + $this->usability['data'][$cnt]['recnum'] = $row[0]; + //$this->usability['data'][$cnt]['usr_id'] = $row['usr_id']; + $this->usability['data'][$cnt]['css'] = $css_usable; + $this->usability['data'][$cnt]['status'] = $status; + + $this->usability['data'][$cnt]['all'] = $row[1]; + + $this->usability['data'][$cnt]['valid'] = $row[2]; + $this->usability['data'][$cnt]['nonsubstantive'] = $row[3]; + $this->usability['data'][$cnt]['nonresponse'] = $row[4]; + + $this->usability['data'][$cnt]['validPercent'] = $row2[2]; + $this->usability['data'][$cnt]['nonsubstantivePercent'] = $row2[3]; + $this->usability['data'][$cnt]['nonresponsePercent'] = $row2[4]; + + $this->usability['data'][$cnt]['breakoff'] = $row[6]; + $this->usability['data'][$cnt]['breakoffPercent'] = $row2[6]; + + $this->usability['data'][$cnt]['usable'] = $row[7]; + $this->usability['data'][$cnt]['usablePercent'] = $row2[7]; + + $this->usability['data'][$cnt]['UNL'] = $row2[17]; + $this->usability['data'][$cnt]['UML'] = $row2[18]; + $this->usability['data'][$cnt]['UCL'] = $row2[19]; + $this->usability['data'][$cnt]['UIL'] = $row2[20]; + $this->usability['data'][$cnt]['UAQ'] = $row2[21]; + + $this->usability['data'][$cnt]['-1'] = $row[9]; + $this->usability['data'][$cnt]['-1_percent'] = $row2[9]; + $this->usability['data'][$cnt]['-2'] = $row[10]; + $this->usability['data'][$cnt]['-2_percent'] = $row2[10]; + $this->usability['data'][$cnt]['-3'] = $row[11]; + $this->usability['data'][$cnt]['-3_percent'] = $row2[11]; + $this->usability['data'][$cnt]['-4'] = $row[12]; + $this->usability['data'][$cnt]['-4_percent'] = $row2[12]; + $this->usability['data'][$cnt]['-5'] = $row[13]; + $this->usability['data'][$cnt]['-5_percent'] = $row2[13]; + $this->usability['data'][$cnt]['-97'] = $row[14]; + $this->usability['data'][$cnt]['-97_percent'] = $row2[14]; + $this->usability['data'][$cnt]['-98'] = $row[15]; + $this->usability['data'][$cnt]['-98_percent'] = $row2[15]; + $this->usability['data'][$cnt]['-99'] = $row[16]; + $this->usability['data'][$cnt]['-99_percent'] = $row2[16]; + + $cnt++; + } + } + + // Sortiramo podatke + foreach ($this->usability['data'] as $key => $row) { + $mid[$key] = $row[$this->sortField]; + } + if($this->sortType == 0) + array_multisort($mid, SORT_ASC, $this->usability['data']); + else + array_multisort($mid, SORT_DESC, $this->usability['data']); + + + # ali odstranimo stolpce kateri imajo same 0 + /*if ($this->show_with_zero == false) { + # odstranimo missinge brez vrednosti + foreach ($this->_missings AS $_key => $_missing) { + if (!isset($this->cols_with_value[$_key]) || $this->cols_with_value[$_key] == false) { + unset($this->_missings[$_key]); + } + } + # odstranimo neveljavne brez vrednosti + foreach ($this->_unsets AS $_key => $_unset) { + if (!isset($this->cols_with_value[$_key]) || $this->cols_with_value[$_key] == false) { + unset($this->_unsets[$_key]); + } + } + }*/ + } + + + // Pripravi csv s podatki + private function prepareDataCSV(){ + global $site_path; + global $lang; + global $admin_type; + + $temp_folder = $site_path . TEMP_FOLDER.'/'; + + $SDF = SurveyDataFile::get_instance(); + $SDF->init($this->anketa); + $_headFileName = $SDF->getHeaderFileName(); + $_dataFileName = $SDF->getDataFileName(); + $_fileStatus = $SDF->getStatus(); + + if ($_headFileName != null && $_headFileName != '') { + $_HEADERS = unserialize(file_get_contents($_headFileName)); + } + else { + echo 'Error! Empty file name!'; + } + + // Zaenkrat dopuscamo samo status 6 in brez lurkerjev + if($admin_type == '0') + $status_filter = '('.STATUS_FIELD.' ~ /6|5/)&&('.LURKER_FIELD.'==0)'; + else + $status_filter = '('.STATUS_FIELD.'==6)&&('.LURKER_FIELD.'==0)'; + + //$start_sequence = $_HEADERS['_settings']['dataSequence']; + $start_sequence = 2; + $end_sequence = $_HEADERS['_settings']['metaSequence']-1; + + $field_delimit = ';'; + + // Filtriramo podatke po statusu in jih zapisemo v temp folder + if (IS_WINDOWS) { + //$command = 'awk -F"|" "BEGIN {{OFS=\",\"} {ORS=\"\n\"}} '.$status_filter.' { print $0}" '.$_dataFileName.' >> '.$temp_folder.'/temp_data_'.$this->anketa.'.dat'; + $out = shell_exec('awk -F"|" "BEGIN {{OFS=\",\"} {ORS=\"\n\"}} '.$status_filter.'" '.$_dataFileName.' | cut -d "|" -f '.$start_sequence.'-'.$end_sequence.' >> '.$temp_folder.'/temp_data_'.$this->anketa.'.dat'); + } + else { + //$command = 'awk -F"|" \'BEGIN {{OFS=","} {ORS="\n"}} '.$status_filter.' { print $0; }\' '.$_dataFileName.' >> '.$temp_folder.'/temp_data_'.$this->anketa.'.dat'; + $out = shell_exec('awk -F"|" \'BEGIN {{OFS=","} {ORS="\n"}} '.$status_filter.'\' '.$_dataFileName.' | cut -d \'|\' -f '.$start_sequence.'-'.$end_sequence.' >> '.$temp_folder.'/temp_data_'.$this->anketa.'.dat'); + } + + + // Ustvarimo koncni CSV + if ($fd = fopen($temp_folder.'/temp_data_'.$this->anketa.'.dat', "r")) { + + $fd2 = fopen($temp_folder.'/data_'.$this->anketa.'.csv', "w"); + + # naredimo header row + foreach ($_HEADERS AS $spid => $spremenljivka) { + if (isset($spremenljivka['grids']) && count($spremenljivka['grids']) > 0) { + foreach ($spremenljivka['grids'] AS $gid => $grid) { + foreach ($grid['variables'] AS $vid => $variable ){ + if ($spremenljivka['tip'] !== 'sm' && !($variable['variable'] == 'uid' && $variable['naslov'] == 'User ID')){ + $output1 .= strip_tags($variable['variable']).$field_delimit; + //$output2 .= '"'.strip_tags($variable['naslov']).'"'.$field_delimit; + } + } + } + } + } + + // Pobrisemo zadnji ; ce obstaja + $output1 = rtrim($output1, ";"); + + // Zapisemo header row + fwrite($fd2, $output1."\r\n"); + //fwrite($fd2, $output2."\r\n"); + + + while ($line = fgets($fd)) { + + //fwrite($fd2, '="'); + //$line = str_replace(array("\r","\n","|"), array("","",'";="'), $line); + $line = '"' . str_replace(array("\r","\n","\"","|"), array("","","",'";"'), $line) . '"'; + + // Spremenimo encoding v windows-1250 + $line = iconv("UTF-8","Windows-1250//TRANSLIT", $line); + //$line = str_replace(array("č","š","ž","Č","Š","Ž"), array("\v{c}","\v{s}","\v{z}","\v{C}","\v{S}","\v{Z}"), $line); + + fwrite($fd2, $line); + //fwrite($fd2, '"'); + fwrite($fd2, "\r\n"); + } + + fclose($fd2); + } + fclose($fd); + + + // Na koncu pobrisemo temp datoteke + if (file_exists($temp_folder.'/temp_data_'.$this->anketa.'.dat')) { + unlink($temp_folder.'/temp_data_'.$this->anketa.'.dat'); + } + } + + // Pripravi csv z vprasanji + private function prepareQuestionCSV(){ + global $site_path; + global $lang; + global $admin_type; + + define('delimiter', ';'); + + $temp_folder = $site_path . TEMP_FOLDER.'/'; + + $fd = fopen($temp_folder.'/questions_'.$this->anketa.'.csv', "w"); + + + // Prva vrstica + $output = ''; + $output .= 'ID SURVEY'.delimiter; + $output .= 'ID QUESTION'.delimiter; + $output .= 'ID PAGE'.delimiter; + $output .= 'QUESTION NUMBER'.delimiter; + + /*$output .= 'naslov'.delimiter; + $output .= 'info'.delimiter;*/ + $output .= 'variable'.delimiter; + /*$output .= 'variable_custom'.delimiter; + $output .= 'label'.delimiter;*/ + $output .= 'tip'.delimiter; + $output .= 'vrstni_red'.delimiter; + //$output .= 'random'.delimiter; + $output .= 'size'.delimiter; + /*$output .= 'undecided'.delimiter; + $output .= 'rejected'.delimiter; + $output .= 'inappropriate'.delimiter; + $output .= 'stat'.delimiter; + $output .= 'orientation'.delimiter; + $output .= 'checkboxhide'.delimiter; + $output .= 'reminder'.delimiter;*/ + $output .= 'visible'.delimiter; + /*$output .= 'textfield'.delimiter; + $output .= 'textfield_label'.delimiter; + $output .= 'cela'.delimiter; + $output .= 'decimalna'.delimiter; + $output .= 'enota'.delimiter; + $output .= 'timer'.delimiter; + $output .= 'sistem'.delimiter; + $output .= 'folder'.delimiter;*/ + $output .= 'params'.delimiter; + /*$output .= 'antonucci'.delimiter; + $output .= 'design'.delimiter; + $output .= 'podpora'.delimiter; + $output .= 'grids'.delimiter; + $output .= 'grids_edit'.delimiter; + $output .= 'grid_subtitle1'.delimiter; + $output .= 'grid_subtitle2'.delimiter; + $output .= 'ranking_k'.delimiter; + $output .= 'vsota'.delimiter; + $output .= 'vsota_limit'.delimiter; + $output .= 'vsota_min'.delimiter; + $output .= 'skala'.delimiter; + $output .= 'vsota_reminder'.delimiter; + $output .= 'vsota_limittype'.delimiter; + $output .= 'vsota_show'.delimiter; + $output .= 'thread'.delimiter; + $output .= 'text_kosov'.delimiter; + $output .= 'text_orientation'.delimiter; + $output .= 'note'.delimiter; + $output .= 'upload'.delimiter; + $output .= 'dostop'.delimiter; + $output .= 'inline_edit'.delimiter; + $output .= 'onchange_submit'.delimiter; + $output .= 'hidden_default'.delimiter; + $output .= 'naslov_graf'.delimiter; + $output .= 'edit_graf'.delimiter; + $output .= 'wide_graf'.delimiter; + $output .= 'coding'.delimiter; + $output .= 'dynamic_mg'.delimiter; + $output .= 'QUESTION IF'.delimiter;*/ + + fwrite($fd, $output."\r\n"); + + + // Vrstice s podatki + $sql = sisplet_query("SELECT s.id, s.gru_id, s.variable, s.tip, s.vrstni_red, s.size, s.visible, s.params + FROM srv_spremenljivka s, srv_grupa g + WHERE s.gru_id=g.id AND g.ank_id='".$this->anketa."' + ORDER BY g.vrstni_red, s.vrstni_red"); + if (!$sql) echo mysqli_error($GLOBALS['connect_db']); + if (mysqli_num_rows($sql) > 0) { + + $i = 0; + + while ($row = mysqli_fetch_array($sql)) { + + $i++; + + $line = ''; + + $line .= $this->anketa.delimiter; + $line .= $row['id'].delimiter; + $line .= $row['gru_id'].delimiter; + $line .= $i.delimiter; + + /*$line .= str_replace("\n", '', str_replace(delimiter, '', $row['naslov']) ).delimiter; + $line .= str_replace("\n", '', str_replace(delimiter, '', $row['info']) ).delimiter;*/ + $line .= $row['variable'].delimiter; + /*$line .= $row['variable_custom'].delimiter; + $line .= str_replace("\n", '', str_replace(delimiter, '', $row['label']) ).delimiter;*/ + $line .= $row['tip'].delimiter; + $line .= $row['vrstni_red'].delimiter; + //$line .= $row['random'].delimiter; + $line .= $row['size'].delimiter; + /*$line .= $row['undecided'].delimiter; + $line .= $row['rejected'].delimiter; + $line .= $row['inappropriate'].delimiter; + $line .= $row['stat'].delimiter; + $line .= $row['orientation'].delimiter; + $line .= $row['checkboxhide'].delimiter; + $line .= $row['reminder'].delimiter;*/ + $line .= $row['visible'].delimiter; + /*$line .= $row['textfield'].delimiter; + $line .= $row['textfield_label'].delimiter; + $line .= $row['cela'].delimiter; + $line .= $row['decimalna'].delimiter; + $line .= $row['enota'].delimiter; + $line .= $row['timer'].delimiter; + $line .= $row['sistem'].delimiter; + $line .= $row['folder'].delimiter;*/ + $line .= str_replace("\n", '', str_replace(delimiter, '', $row['params']) ).delimiter; + /*$line .= $row['antonucci'].delimiter; + $line .= $row['design'].delimiter; + $line .= $row['podpora'].delimiter; + $line .= $row['grids'].delimiter; + $line .= $row['grids_edit'].delimiter; + $line .= $row['grid_subtitle1'].delimiter; + $line .= $row['grid_subtitle2'].delimiter; + $line .= $row['ranking_k'].delimiter; + $line .= $row['vsota'].delimiter; + $line .= $row['vsota_limit'].delimiter; + $line .= $row['vsota_min'].delimiter; + $line .= $row['skala'].delimiter; + $line .= $row['vsota_reminder'].delimiter; + $line .= $row['vsota_limittype'].delimiter; + $line .= $row['vsota_show'].delimiter; + $line .= $row['thread'].delimiter; + $line .= $row['text_kosov'].delimiter; + $line .= $row['text_orientation'].delimiter; + $line .= str_replace("\n", '', str_replace(delimiter, '', $row['note']) ).delimiter; + $line .= $row['upload'].delimiter; + $line .= $row['dostop'].delimiter; + $line .= $row['inline_edit'].delimiter; + $line .= $row['onchange_submit'].delimiter; + $line .= $row['hidden_default'].delimiter; + $line .= str_replace("\n", '', str_replace(delimiter, '', $row['naslov_graf']) ).delimiter; + $line .= $row['edit_graf'].delimiter; + $line .= $row['wide_graf'].delimiter; + $line .= $row['coding'].delimiter; + $line .= $row['dynamic_mg'].delimiter; + + $sql1 = sisplet_query("SELECT f.* FROM srv_branching b, srv_if f WHERE b.element_spr = '$row[id]' AND b.parent=f.id"); + $row1 = mysqli_fetch_array($sql1); + $line .= $this->parentIf($id, $row1['id']).delimiter;*/ + + fwrite($fd, $line."\r\n"); + } + } + + fclose($fd); + } + + // Pripravi csv z itemi + private function prepareItemCSV(){ + global $site_path; + global $lang; + global $admin_type; + + define('delimiter', ';'); + + $temp_folder = $site_path . TEMP_FOLDER.'/'; + + $fd = fopen($temp_folder.'/items_'.$this->anketa.'.csv', "w"); + + // Prva vrstica + $output = ''; + $output .= 'ID SURVEY'.delimiter; + $output .= 'ID QUESTION'.delimiter; + $output .= 'ID ITEM'.delimiter; + + /*$output .= 'naslov'.delimiter; + $output .= 'naslov2'.delimiter;*/ + $output .= 'variable'.delimiter; + $output .= 'variable_custom'.delimiter; + $output .= 'vrstni_red'.delimiter; + /*$output .= 'random'.delimiter; + $output .= 'other'.delimiter; + $output .= 'if_id'.delimiter; + $output .= 'size'.delimiter; + $output .= 'naslov_graf'.delimiter; + + $output .= 'grid_vrstni_red'.delimiter; + $output .= 'grid_variable'.delimiter; + $output .= 'grid_other'.delimiter; + $output .= 'grid_naslov'.delimiter;*/ + + fwrite($fd, $output."\r\n"); + + + // Vrstice s podatki + $sql = sisplet_query("SELECT v.id, v.spr_id, v.variable, v.variable_custom, v.vrstni_red + FROM srv_vrednost v, srv_spremenljivka s, srv_grupa g + WHERE v.spr_id=s.id AND s.gru_id=g.id AND g.ank_id='".$this->anketa."' + ORDER BY g.vrstni_red, s.vrstni_red"); + if (!$sql) echo mysqli_error($GLOBALS['connect_db']); + if (mysqli_num_rows($sql) > 0) { + + while ($row = mysqli_fetch_array($sql)) { + + $line = ''; + + $line .= $this->anketa.delimiter; + $line .= $row['spr_id'].delimiter; + $line .= $row['id'].delimiter; + + /*$line .= str_replace("\n", '', str_replace(delimiter, '', $row['naslov']) ).delimiter; + $line .= str_replace("\n", '', str_replace(delimiter, '', $row['naslov2']) ).delimiter;*/ + $line .= str_replace("\n", '', str_replace(delimiter, '', $row['variable']) ).delimiter; + $line .= $row['variable_custom'].delimiter; + $line .= $row['vrstni_red'].delimiter; + /*$line .= $row['random'].delimiter; + $line .= $row['other'].delimiter; + $line .= $row['if_id'].delimiter; + $line .= $row['size'].delimiter; + $line .= str_replace("\n", '', str_replace(delimiter, '', $row['naslov_graf']) ).delimiter; + + $sql1 = sisplet_query("SELECT * FROM srv_grid WHERE spr_id = '$row[spr_id]' ORDER BY vrstni_red ASC"); + while ($row1 = mysqli_fetch_array($sql1)) { + + $line .= str_replace("\n", '', str_replace(delimiter, '', $row1['vrstni_red']) ).delimiter; + $line .= str_replace("\n", '', str_replace(delimiter, '', $row1['variable']) ).delimiter; + $line .= str_replace("\n", '', str_replace(delimiter, '', $row1['other']) ).delimiter; + $line .= str_replace("\n", '', str_replace(delimiter, '', $row1['naslov']) ).delimiter; + }*/ + + fwrite($fd, $line."\r\n"); + } + } + + fclose($fd); + } + + + // Pobrisemo zacasne datoteke + private function deleteTemp(){ + global $site_path; + + $temp_folder = $site_path . TEMP_FOLDER.'/'; + $result_folder = $site_path . RESULTS_FOLDER.'/'; + + // Pobrisemo zacasno CSV datoteko s podatki + if (file_exists($temp_folder.'/data_'.$this->anketa.'.csv')) { + unlink($temp_folder.'/data_'.$this->anketa.'.csv'); + } + + // Pobrisemo zacasno CSV datoteko z vprasanji + if (file_exists($temp_folder.'/questions_'.$this->anketa.'.csv')) { + unlink($temp_folder.'/questions_'.$this->anketa.'.csv'); + } + + // Pobrisemo zacasno CSV datoteko z itemi + if (file_exists($temp_folder.'/items_'.$this->anketa.'.csv')) { + unlink($temp_folder.'/items_'.$this->anketa.'.csv'); + } + + // Pobrisemo CSV datoteko z rezultati + if (file_exists($result_folder.'/usability_'.$this->anketa.'.csv')) { + unlink($result_folder.'/usability_'.$this->anketa.'.csv'); + } + } + + + private function parentIf($anketa, $element) { + $sql = sisplet_query("SELECT tip FROM srv_if WHERE id = '$element'"); + $row = mysqli_fetch_array($sql); + + if ($row['tip'] == 0) return $element; + + $sql1 = sisplet_query("SELECT parent FROM srv_branching WHERE ank_id='$anketa' AND element_if = '$element'"); + $row1 = mysqli_fetch_array($sql1); + + return parentIf($anketa, $row1['parent']); + } + + + /** Funkcija ki nastavi vse filtre + * + */ + private function setUpFilter(){ + /*if ($this->dataFileStatus == FILE_STATUS_NO_DATA + || $this->dataFileStatus == FILE_STATUS_NO_FILE + || $this->dataFileStatus == FILE_STATUS_SRV_DELETED) + { + return false; + }*/ + + # poiščemo kater profil uporablja uporabnik + $_currentMissingProfile = SurveyUserSetting :: getInstance()->getSettings('default_missing_profile'); + $this->currentMissingProfile = (isset($_currentMissingProfile) ? $_currentMissingProfile : 1); + + # filtriranje po statusih + $this->_CURRENT_STATUS_FILTER = SurveyStatusProfiles :: getStatusAsAWKString(); + + # filtriranje po časih + $_time_profile_awk = SurveyTimeProfiles :: getFilterForAWK($this->_HEADERS['unx_ins_date']['grids']['0']['variables']['0']['sequence']); + + # dodamo še ife + + SurveyConditionProfiles :: setHeader($this->_HEADERS); + $_condition_profile_AWK = SurveyConditionProfiles:: getAwkConditionString(); + + if (($_condition_profile_AWK != "" && $_condition_profile_AWK != null ) + || ($_time_profile_awk != "" && $_time_profile_awk != null)) + { + $this->_CURRENT_STATUS_FILTER = '('.$this->_CURRENT_STATUS_FILTER; + if ($_condition_profile_AWK != "" && $_condition_profile_AWK != null ) + { + $this->_CURRENT_STATUS_FILTER .= ' && '.$_condition_profile_AWK; + } + if ($_time_profile_awk != "" && $_time_profile_awk != null) + { + $this->_CURRENT_STATUS_FILTER .= ' && '.$_time_profile_awk; + } + $this->_CURRENT_STATUS_FILTER .= ')'; + } + $status_filter = $this->_CURRENT_STATUS_FILTER; + + if ($this->dataFileStatus == FILE_STATUS_OK || $this->dataFileStatus == FILE_STATUS_OLD) + { + if (isset($this->_HEADERS['testdata'])) + { + $this->_HAS_TEST_DATA = true; + } + } + + $smv = new SurveyMissingValues($this->anketa); + $smv -> Init(); + + $smv_array = $smv->GetSurveyMissingValues($this->anketa); + if (!empty($smv_array[1])){ + foreach ($smv_array[1] AS $_survey_missings) + { + $this->_missings[$_survey_missings['value']] = $_survey_missings['text']; + + } + } + if (!empty($smv_array[2])){ + foreach ($smv_array[2] AS $_survey_unsets) + { + $this->_unsets[$_survey_unsets['value']] = $_survey_unsets['text']; + } + } + } + + private function setUserFilters(){ + # Nastavimo filtre variabel + $dvp = SurveyUserSetting :: getInstance()->getSettings('default_variable_profile'); + $_currentVariableProfile = SurveyVariablesProfiles :: checkDefaultProfile($dvp); + if ($dvp != $_currentVariableProfile) { + SurveyUserSetting :: getInstance()->saveSettings('default_variable_profile', $_currentVariableProfile); + } + $this->_PROFILE_ID_VARIABLE = $_currentVariableProfile; + + # ali prikazujemo tudi stolpce z 0 vrednostmi + if (isset($this->sessionData['usable_resp']['show_with_zero'])) { + $this->show_with_zero = $this->sessionData['usable_resp']['show_with_zero']; + } + + # ali prikazujemo tudi stolpce z 0 vrednostmi + if (isset($this->sessionData['usable_resp']['show_details'])) { + $this->show_details = $this->sessionData['usable_resp']['show_details']; + } + + # ali prikazujemo tudi stolpce z izracuni + if (isset($this->sessionData['usable_resp']['show_calculations'])) { + $this->show_calculations = $this->sessionData['usable_resp']['show_calculations']; + } + + # ali prikazujemo vrstice "Drugo" + $this->show_with_other = true; + if (isset($this->sessionData['usable_resp']['show_with_other'])) { + $this->show_with_other = $this->sessionData['usable_resp']['show_with_other']; + } + + # ali prikazujemo vrstice tipa "besedilo" + $this->show_with_text = true; + if (isset($this->sessionData['usable_resp']['show_with_text'])) { + $this->show_with_text = $this->sessionData['usable_resp']['show_with_text']; + } + + # Spodnja in zgornja meja za usable + if (isset($this->sessionData['usable_resp']['bottom_usable_limit'])) { + $this->bottom_usable_limit = $this->sessionData['usable_resp']['bottom_usable_limit']; + } + # ali prikazujemo tudi stolpce z 0 vrednostmi + if (isset($this->sessionData['usable_resp']['top_usable_limit'])) { + $this->top_usable_limit = $this->sessionData['usable_resp']['top_usable_limit']; + } + } + + // Ali imamo zgenerirano datoteko ali ne + private function hasDataFile(){ + if ($this->dataFileStatus == FILE_STATUS_NO_DATA || $this->dataFileStatus == FILE_STATUS_NO_FILE + || $this->dataFileStatus == FILE_STATUS_SRV_DELETED) + return false; + else + return true; + } + + private function setStatusFilter($status=''){ + + $this->_CURRENT_STATUS_FILTER = $status; + } + + + +} \ No newline at end of file diff --git a/admin/survey/modules/mod_kakovost/results/.gitignore b/admin/survey/modules/mod_kakovost/results/.gitignore new file mode 100644 index 000000000..c96a04f00 --- /dev/null +++ b/admin/survey/modules/mod_kakovost/results/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/admin/survey/modules/mod_kakovost/temp/.gitignore b/admin/survey/modules/mod_kakovost/temp/.gitignore new file mode 100644 index 000000000..c96a04f00 --- /dev/null +++ b/admin/survey/modules/mod_kakovost/temp/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/composer.lock b/composer.lock index 0b2d2ed86..44d5aa3ba 100644 --- a/composer.lock +++ b/composer.lock @@ -308,16 +308,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "1.8.1", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "35ea11d335fd638b5882ff1725228b3d35496ab1" + "reference": "dc960a912984efb74d0a90222870c72c87f10c91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/35ea11d335fd638b5882ff1725228b3d35496ab1", - "reference": "35ea11d335fd638b5882ff1725228b3d35496ab1", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91", + "reference": "dc960a912984efb74d0a90222870c72c87f10c91", "shasum": "" }, "require": { @@ -375,7 +375,7 @@ "uri", "url" ], - "time": "2021-03-21 16:25:00" + "time": "2021-04-26 09:17:50" }, { "name": "maxmind-db/reader", @@ -757,16 +757,16 @@ }, { "name": "phpmailer/phpmailer", - "version": "v6.4.0", + "version": "v6.4.1", "source": { "type": "git", "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "050d430203105c27c30efd1dce7aa421ad882d01" + "reference": "9256f12d8fb0cd0500f93b19e18c356906cbed3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/050d430203105c27c30efd1dce7aa421ad882d01", - "reference": "050d430203105c27c30efd1dce7aa421ad882d01", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/9256f12d8fb0cd0500f93b19e18c356906cbed3d", + "reference": "9256f12d8fb0cd0500f93b19e18c356906cbed3d", "shasum": "" }, "require": { @@ -819,7 +819,7 @@ } ], "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", diff --git a/lang/1.php b/lang/1.php index 2bbda983e..dedfde1de 100644 --- a/lang/1.php +++ b/lang/1.php @@ -6355,6 +6355,7 @@ $lang = array ( 'srv_off' => 'Izklopi', 'srv_para_neodgovori' => 'Neodgovor spremenljivke', 'srv_usable_respondents' => 'Uporabni respondenti', + 'srv_kakovost' => 'Kakovost respondentov', '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. Več >>', 'srv_speeder_index_legend_0' => '0 - ni prehiter', diff --git a/lang/2.php b/lang/2.php index 4e12f4302..ffe5e8d02 100644 --- a/lang/2.php +++ b/lang/2.php @@ -6240,6 +6240,7 @@ $lang = array ( 'srv_off' => 'Turn off', 'srv_para_neodgovori' => 'Item nonresponse', 'srv_usable_respondents' => 'Usable respondents', + 'srv_kakovost' => 'Respondents quality', '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. More >>', 'srv_speeder_index_legend_0' => '0 - not too fast', diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 77635b59a..32cab0dd9 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -950,6 +950,7 @@ return array( 'SurveyInvitationsNew' => $baseDir . '/admin/survey/classes/surveyEmails/class.SurveyInvitationsNew.php', 'SurveyInvitationsSqualo' => $baseDir . '/admin/survey/modules/mod_squalo/class.SurveyInvitationsSqualo.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', 'SurveyList' => $baseDir . '/admin/survey/classes/class.SurveyList.php', 'SurveyLog' => $baseDir . '/admin/survey/classes/log/class.SurveyLog.php', diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index e7e999702..fd450d20d 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -11,8 +11,8 @@ return array( 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php', 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php', - 'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php', '3109cb1a231dcd04bee1f9f620d46975' => $vendorDir . '/paragonie/sodium_compat/autoload.php', + 'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', '2bf0564df058fbe21b5554296cc8571a' => $baseDir . '/main/survey/class.EvalvacijaMain.php', '5bc35216aa4f6cb823ce4a5cec52fdce' => $baseDir . '/main/survey/mobile-detect/Mobile_Detect.php', diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index c3e8b10a2..a2d963843 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -12,8 +12,8 @@ class ComposerStaticInit6b03163c371c5541881b55b762b8c779 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php', 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php', - 'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php', '3109cb1a231dcd04bee1f9f620d46975' => __DIR__ . '/..' . '/paragonie/sodium_compat/autoload.php', + 'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', '2bf0564df058fbe21b5554296cc8571a' => __DIR__ . '/../..' . '/main/survey/class.EvalvacijaMain.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', 'SurveyInvitationsSqualo' => __DIR__ . '/../..' . '/admin/survey/modules/mod_squalo/class.SurveyInvitationsSqualo.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', 'SurveyList' => __DIR__ . '/../..' . '/admin/survey/classes/class.SurveyList.php', 'SurveyLog' => __DIR__ . '/../..' . '/admin/survey/classes/log/class.SurveyLog.php', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 491de3572..699855b1f 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -1615,147 +1615,6 @@ "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", "version": "v7.77.0", @@ -1961,5 +1820,146 @@ "secret-key cryptography", "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" + ] } ] diff --git a/vendor/guzzlehttp/psr7/.github/workflows/static.yml b/vendor/guzzlehttp/psr7/.github/workflows/static.yml new file mode 100644 index 000000000..ab4d68ba3 --- /dev/null +++ b/vendor/guzzlehttp/psr7/.github/workflows/static.yml @@ -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 diff --git a/vendor/guzzlehttp/psr7/CHANGELOG.md b/vendor/guzzlehttp/psr7/CHANGELOG.md index a608aa419..6a6b7bf98 100644 --- a/vendor/guzzlehttp/psr7/CHANGELOG.md +++ b/vendor/guzzlehttp/psr7/CHANGELOG.md @@ -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). -## [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 - Issue parsing IPv6 URLs - Issue modifying ServerRequest lost all its attributes -## [1.8.0] - 2021-03-21 +## 1.8.0 - 2021-03-21 ### 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 - Broken `Utils::tryFopen()` on PHP 8 -## [1.7.0] - 2020-09-30 +## 1.7.0 - 2020-09-30 ### 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.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 diff --git a/vendor/guzzlehttp/psr7/src/Uri.php b/vendor/guzzlehttp/psr7/src/Uri.php index ab201c1d1..0f9f020d3 100644 --- a/vendor/guzzlehttp/psr7/src/Uri.php +++ b/vendor/guzzlehttp/psr7/src/Uri.php @@ -109,7 +109,7 @@ class Uri implements UriInterface $url ); - $result = parse_url($prefix.$encodedUrl); + $result = parse_url($prefix . $encodedUrl); if ($result === false) { return false; diff --git a/vendor/guzzlehttp/psr7/src/Utils.php b/vendor/guzzlehttp/psr7/src/Utils.php index 7c7cc3eb6..6b6c8cced 100644 --- a/vendor/guzzlehttp/psr7/src/Utils.php +++ b/vendor/guzzlehttp/psr7/src/Utils.php @@ -217,7 +217,7 @@ final class Utils ->withQueryParams($request->getQueryParams()) ->withCookieParams($request->getCookieParams()) ->withUploadedFiles($request->getUploadedFiles()); - + foreach ($request->getAttributes() as $key => $value) { $new = $new->withAttribute($key, $value); } @@ -317,7 +317,8 @@ final class Utils * The 'php://input' is a special stream with quirks and inconsistencies. * 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+'); fwrite($stream, stream_get_contents($resource)); fseek($stream, 0); diff --git a/vendor/phpmailer/phpmailer/SECURITY.md b/vendor/phpmailer/phpmailer/SECURITY.md index 2552df8a3..5a6a8f41c 100644 --- a/vendor/phpmailer/phpmailer/SECURITY.md +++ b/vendor/phpmailer/phpmailer/SECURITY.md @@ -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. +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 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. diff --git a/vendor/phpmailer/phpmailer/VERSION b/vendor/phpmailer/phpmailer/VERSION index c0be8a799..306894a15 100644 --- a/vendor/phpmailer/phpmailer/VERSION +++ b/vendor/phpmailer/phpmailer/VERSION @@ -1 +1 @@ -6.4.0 \ No newline at end of file +6.4.1 \ No newline at end of file diff --git a/vendor/phpmailer/phpmailer/composer.json b/vendor/phpmailer/phpmailer/composer.json index b8aa5cfcc..58393b2cd 100644 --- a/vendor/phpmailer/phpmailer/composer.json +++ b/vendor/phpmailer/phpmailer/composer.json @@ -57,5 +57,9 @@ "PHPMailer\\Test\\": "test/" } }, - "license": "LGPL-2.1-only" + "license": "LGPL-2.1-only", + "scripts": { + "check": "./vendor/bin/phpcs", + "test": "./vendor/bin/phpunit" + } } diff --git a/vendor/phpmailer/phpmailer/language/phpmailer.lang-uk.php b/vendor/phpmailer/phpmailer/language/phpmailer.lang-uk.php index 9fa60cf19..3dea055bb 100644 --- a/vendor/phpmailer/phpmailer/language/phpmailer.lang-uk.php +++ b/vendor/phpmailer/phpmailer/language/phpmailer.lang-uk.php @@ -16,11 +16,11 @@ $PHPMAILER_LANG['file_access'] = 'Немає доступу до фай $PHPMAILER_LANG['file_open'] = 'Помилка файлової системи: не вдається відкрити файл: '; $PHPMAILER_LANG['from_failed'] = 'Невірна адреса відправника: '; $PHPMAILER_LANG['instantiate'] = 'Неможливо запустити функцію mail().'; -$PHPMAILER_LANG['provide_address'] = 'Будь-ласка, введіть хоча б одну email-адресу отримувача.'; +$PHPMAILER_LANG['provide_address'] = 'Будь ласка, введіть хоча б одну email-адресу отримувача.'; $PHPMAILER_LANG['mailer_not_supported'] = ' - поштовий сервер не підтримується.'; $PHPMAILER_LANG['recipients_failed'] = 'Помилка SMTP: не вдалося відправлення для таких отримувачів: '; $PHPMAILER_LANG['empty_message'] = 'Пусте повідомлення'; -$PHPMAILER_LANG['invalid_address'] = 'Не відправлено через невірний формат email-адреси: '; +$PHPMAILER_LANG['invalid_address'] = 'Не відправлено через неправильний формат email-адреси: '; $PHPMAILER_LANG['signing'] = 'Помилка підпису: '; $PHPMAILER_LANG['smtp_connect_failed'] = 'Помилка з\'єднання з SMTP-сервером'; $PHPMAILER_LANG['smtp_error'] = 'Помилка SMTP-сервера: '; diff --git a/vendor/phpmailer/phpmailer/src/PHPMailer.php b/vendor/phpmailer/phpmailer/src/PHPMailer.php index 1a65cfd8d..2d16116a8 100644 --- a/vendor/phpmailer/phpmailer/src/PHPMailer.php +++ b/vendor/phpmailer/phpmailer/src/PHPMailer.php @@ -748,7 +748,7 @@ class PHPMailer * * @var string */ - const VERSION = '6.4.0'; + const VERSION = '6.4.1'; /** * Error severity: message only, continue processing. @@ -1721,9 +1721,10 @@ class PHPMailer fwrite($mail, $header); fwrite($mail, $body); $result = pclose($mail); + $addrinfo = static::parseAddresses($toAddr); $this->doCallback( ($result === 0), - [$toAddr], + [[$addrinfo['address'], $addrinfo['name']]], $this->cc, $this->bcc, $this->Subject, @@ -1810,7 +1811,8 @@ class PHPMailer */ 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) { + if (!static::isPermittedPath($path)) { + return false; + } $readable = file_exists($path); //If not a UNC path (expected to start with \\), check read permission, see #2069 if (strpos($path, '\\\\') !== 0) { $readable = $readable && is_readable($path); } - return static::isPermittedPath($path) && $readable; + return $readable; } /** @@ -1876,7 +1881,17 @@ class PHPMailer if ($this->SingleTo && count($toArr) > 1) { foreach ($toArr as $toAddr) { $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 { $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params); @@ -1965,7 +1980,7 @@ class PHPMailer $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) { $this->doCallback( $cb['issent'], - [$cb['to']], + [[$cb['to'], $cb['name']]], [], [], $this->Subject, diff --git a/vendor/phpmailer/phpmailer/src/POP3.php b/vendor/phpmailer/phpmailer/src/POP3.php index 9bd319865..a39f4803c 100644 --- a/vendor/phpmailer/phpmailer/src/POP3.php +++ b/vendor/phpmailer/phpmailer/src/POP3.php @@ -46,7 +46,7 @@ class POP3 * * @var string */ - const VERSION = '6.4.0'; + const VERSION = '6.4.1'; /** * Default POP3 port number. diff --git a/vendor/phpmailer/phpmailer/src/SMTP.php b/vendor/phpmailer/phpmailer/src/SMTP.php index 9d85929dd..0e7f53df5 100644 --- a/vendor/phpmailer/phpmailer/src/SMTP.php +++ b/vendor/phpmailer/phpmailer/src/SMTP.php @@ -35,7 +35,7 @@ class SMTP * * @var string */ - const VERSION = '6.4.0'; + const VERSION = '6.4.1'; /** * SMTP line break constant.