diff --git a/assets/knet-icon.png b/assets/knet-icon.png
new file mode 100644
index 0000000..5c655f3
Binary files /dev/null and b/assets/knet-icon.png differ
diff --git a/assets/knet-logo.png b/assets/knet-logo.png
new file mode 100644
index 0000000..fd5645a
Binary files /dev/null and b/assets/knet-logo.png differ
diff --git a/classes/SimpleXLSXGen.php b/classes/SimpleXLSXGen.php
new file mode 100644
index 0000000..ecf272a
--- /dev/null
+++ b/classes/SimpleXLSXGen.php
@@ -0,0 +1,667 @@
+curSheet = -1;
+ $this->defaultFont = 'Calibri';
+ $this->sheets = [ ['name' => 'Sheet1', 'rows' => [], 'hyperlinks' => [] ] ];
+ $this->SI = []; // sharedStrings index
+ $this->SI_KEYS = []; // & keys
+ $this->F = [ self::F_NORMAL ]; // fonts
+ $this->F_KEYS = [0]; // & keys
+ $this->XF = [ [self::N_NORMAL, self::F_NORMAL, self::A_DEFAULT] ]; // styles
+ $this->XF_KEYS = ['N0F0A0' => 0 ]; // & keys
+
+ $this->template = [
+ '_rels/.rels' => '
+
+
+
+
+',
+ 'docProps/app.xml' => '
+
+0
+'.__CLASS__.'',
+ 'docProps/core.xml' => '
+
+{DATE}
+en-US
+{DATE}
+1
+',
+ 'xl/_rels/workbook.xml.rels' => '
+
+
+{SHEETS}',
+ 'xl/worksheets/sheet1.xml' => '
+{COLS}{ROWS}{HYPERLINKS}',
+ 'xl/worksheets/_rels/sheet1.xml.rels' => '
+{HYPERLINKS}',
+ 'xl/sharedStrings.xml' => '
+{STRINGS}',
+ 'xl/styles.xml' => '
+
+{FONTS}
+
+
+
+{XF}
+
+
+
+',
+ 'xl/workbook.xml' => '
+
+
+{SHEETS}
+',
+ '[Content_Types].xml' => '
+
+
+
+
+
+
+
+
+{TYPES}
+',
+ ];
+
+ //
+ // 0100
1200
+ // Простой шаблонБудем делать генератор
+ }
+ public static function fromArray( array $rows, $sheetName = null ) {
+ $xlsx = new static();
+ return $xlsx->addSheet( $rows, $sheetName );
+ }
+
+ public function addSheet( array $rows, $name = null ) {
+
+ $this->curSheet++;
+ if ( $name === null ) { // autogenerated sheet names
+ $name = 'Sheet'.($this->curSheet+1);
+ } else {
+ $names = [];
+ foreach( $this->sheets as $sh ) {
+ $names[ mb_strtoupper( $sh['name']) ] = 1;
+ }
+ for( $i = 0; $i < 100; $i++ ) {
+ $new_name = ($i === 0) ? $name : $name .' ('.$i.')';
+ $NEW_NAME = mb_strtoupper( $new_name );
+ if ( !isset( $names[ $NEW_NAME ]) ) {
+ $name = $new_name;
+ break;
+ }
+ }
+ }
+
+ $this->sheets[$this->curSheet] = ['name' => $name, 'hyperlinks' => []];
+
+ if ( is_array( $rows ) && isset( $rows[0] ) && is_array($rows[0]) ) {
+ $this->sheets[$this->curSheet]['rows'] = $rows;
+ } else {
+ $this->sheets[$this->curSheet]['rows'] = [];
+ }
+ return $this;
+ }
+
+ public function __toString() {
+ $fh = fopen( 'php://memory', 'wb' );
+ if ( ! $fh ) {
+ return '';
+ }
+
+ if ( ! $this->_write( $fh ) ) {
+ fclose( $fh );
+ return '';
+ }
+ $size = ftell( $fh );
+ fseek( $fh, 0);
+
+ return (string) fread( $fh, $size );
+ }
+
+ public function saveAs( $filename ) {
+ $fh = fopen( $filename, 'wb' );
+ if (!$fh) {
+ return false;
+ }
+ if ( !$this->_write($fh) ) {
+ fclose($fh);
+ return false;
+ }
+ fclose($fh);
+
+ return true;
+ }
+
+ public function download() {
+ return $this->downloadAs( gmdate('YmdHi') . '.xlsx' );
+ }
+
+ public function downloadAs( $filename ) {
+ $fh = fopen('php://memory','wb');
+ if (!$fh) {
+ return false;
+ }
+
+ if ( !$this->_write( $fh )) {
+ fclose( $fh );
+ return false;
+ }
+
+ $size = ftell($fh);
+
+ header('Content-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+ header('Content-Disposition: attachment; filename="'.$filename.'"');
+ header('Last-Modified: ' . gmdate('D, d M Y H:i:s \G\M\T' , time() ));
+ header('Content-Length: '.$size);
+
+ while( ob_get_level() ) {
+ ob_end_clean();
+ }
+ fseek($fh,0);
+ fpassthru( $fh );
+
+ fclose($fh);
+ return true;
+ }
+
+ protected function _write( $fh ) {
+
+
+ $dirSignatureE= "\x50\x4b\x05\x06"; // end of central dir signature
+ $zipComments = 'Generated by '.__CLASS__.' PHP class, thanks sergey.shuchkin@gmail.com';
+
+ if (!$fh) {
+ return false;
+ }
+
+ $cdrec = ''; // central directory content
+ $entries= 0; // number of zipped files
+ $cnt_sheets = count( $this->sheets );
+
+ foreach ($this->template as $cfilename => $template ) {
+ if ( $cfilename === 'xl/_rels/workbook.xml.rels' ) {
+ $s = '';
+ for ( $i = 0; $i < $cnt_sheets; $i++) {
+ $s .= '\n";
+ }
+ $s .= '';
+ $template = str_replace('{SHEETS}', $s, $template);
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ } elseif ( $cfilename === 'xl/workbook.xml' ) {
+ $s = '';
+ foreach ( $this->sheets as $k => $v ) {
+ $s .= '';
+ }
+ $template = str_replace('{SHEETS}', $s, $template);
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ }
+ elseif ( $cfilename === 'docProps/core.xml' ) {
+ $template = str_replace('{DATE}', gmdate('Y-m-d\TH:i:s\Z'), $template);
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ } elseif ( $cfilename === 'xl/sharedStrings.xml' ) {
+ if (!count($this->SI)) {
+ $this->SI[] = 'No Data';
+ }
+ $si_cnt = count($this->SI);
+ $si = ''.implode("\r\n", $this->SI).'';
+ $template = str_replace(['{CNT}', '{STRINGS}'], [ $si_cnt, $si ], $template );
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ } elseif ( $cfilename === 'xl/worksheets/sheet1.xml' ) {
+ foreach ( $this->sheets as $k => $v ) {
+ $filename = 'xl/worksheets/sheet'.($k+1).'.xml';
+ $xml = $this->_sheetToXML($k, $template);
+ $this->_writeEntry($fh, $cdrec, $filename, $xml );
+ $entries++;
+ }
+ $xml = null;
+ } elseif ( $cfilename === 'xl/worksheets/_rels/sheet1.xml.rels' ) {
+ foreach ( $this->sheets as $k => $v ) {
+ if ( count($v['hyperlinks'])) {
+ $RH = [];
+ $filename = 'xl/worksheets/_rels/sheet' . ( $k + 1 ) . '.xml.rels';
+ foreach ( $v['hyperlinks'] as $h ) {
+ $RH[] = '';
+ }
+ $xml = str_replace( '{HYPERLINKS}', implode( "\r\n", $RH ), $template );
+ $this->_writeEntry( $fh, $cdrec, $filename, $xml );
+ $entries++;
+ }
+ }
+ $xml = null;
+
+ } elseif ( $cfilename === '[Content_Types].xml' ) {
+ $TYPES = [''];
+ foreach ( $this->sheets as $k => $v) {
+ $TYPES[] = '';
+ if (count( $v['hyperlinks'])) {
+ $TYPES[] = '';
+ }
+ }
+ $template = str_replace('{TYPES}', implode("\r\n", $TYPES), $template);
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ } elseif ( $cfilename === 'xl/styles.xml' ) {
+ $FONTS = [''];
+ foreach ( $this->F as $f ) {
+ $FONTS[] = ''
+ . ( $this->defaultFontSize ? '' : '' )
+ .( $f & self::F_BOLD ? '' : '')
+ .( $f & self::F_ITALIC ? '' : '')
+ .( $f & self::F_UNDERLINE ? '' : '')
+ .( $f & self::F_STRIKE ? '' : '')
+ .( $f & self::F_HYPERLINK ? '' : '')
+ .'';
+ }
+ $FONTS[] = '';
+ $XF = [''];
+ foreach( $this->XF as $xf ) {
+ $align = ($xf[2] === self::A_LEFT ? ' applyAlignment="1">' : '')
+ .($xf[2] === self::A_RIGHT ? ' applyAlignment="1">' : '')
+ .($xf[2] === self::A_CENTER ? ' applyAlignment="1">' : '');
+ $XF[] = ' 0 ? ' applyNumberFormat="1"' : '')
+ .($align ? $align . '' : '/>');
+
+ }
+ $XF[] = '';
+ $template = str_replace(['{FONTS}','{XF}'], [implode("\r\n", $FONTS), implode("\r\n", $XF)], $template);
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ } else {
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ }
+ }
+ $before_cd = ftell($fh);
+ fwrite($fh, $cdrec);
+
+ // end of central dir
+ fwrite($fh, $dirSignatureE);
+ fwrite($fh, pack('v', 0)); // number of this disk
+ fwrite($fh, pack('v', 0)); // number of the disk with the start of the central directory
+ fwrite($fh, pack('v', $entries)); // total # of entries "on this disk"
+ fwrite($fh, pack('v', $entries)); // total # of entries overall
+ fwrite($fh, pack('V', mb_strlen($cdrec,'8bit'))); // size of central dir
+ fwrite($fh, pack('V', $before_cd)); // offset to start of central dir
+ fwrite($fh, pack('v', mb_strlen($zipComments,'8bit'))); // .zip file comment length
+ fwrite($fh, $zipComments);
+
+ return true;
+ }
+
+ protected function _writeEntry($fh, &$cdrec, $cfilename, $data) {
+ $zipSignature = "\x50\x4b\x03\x04"; // local file header signature
+ $dirSignature = "\x50\x4b\x01\x02"; // central dir header signature
+
+ $e = [];
+ $e['uncsize'] = mb_strlen($data, '8bit');
+
+ // if data to compress is too small, just store it
+ if($e['uncsize'] < 256){
+ $e['comsize'] = $e['uncsize'];
+ $e['vneeded'] = 10;
+ $e['cmethod'] = 0;
+ $zdata = $data;
+ } else{ // otherwise, compress it
+ $zdata = gzcompress($data);
+ $zdata = substr(substr($zdata, 0, - 4 ), 2); // fix crc bug (thanks to Eric Mueller)
+ $e['comsize'] = mb_strlen($zdata, '8bit');
+ $e['vneeded'] = 10;
+ $e['cmethod'] = 8;
+ }
+
+ $e['bitflag'] = 0;
+ $e['crc_32'] = crc32($data);
+
+ // Convert date and time to DOS Format, and set then
+ $lastmod_timeS = str_pad(decbin(date('s')>=32?date('s')-32:date('s')), 5, '0', STR_PAD_LEFT);
+ $lastmod_timeM = str_pad(decbin(date('i')), 6, '0', STR_PAD_LEFT);
+ $lastmod_timeH = str_pad(decbin(date('H')), 5, '0', STR_PAD_LEFT);
+ $lastmod_dateD = str_pad(decbin(date('d')), 5, '0', STR_PAD_LEFT);
+ $lastmod_dateM = str_pad(decbin(date('m')), 4, '0', STR_PAD_LEFT);
+ $lastmod_dateY = str_pad(decbin(date('Y')-1980), 7, '0', STR_PAD_LEFT);
+
+ # echo "ModTime: $lastmod_timeS-$lastmod_timeM-$lastmod_timeH (".date("s H H").")\n";
+ # echo "ModDate: $lastmod_dateD-$lastmod_dateM-$lastmod_dateY (".date("d m Y").")\n";
+ $e['modtime'] = bindec("$lastmod_timeH$lastmod_timeM$lastmod_timeS");
+ $e['moddate'] = bindec("$lastmod_dateY$lastmod_dateM$lastmod_dateD");
+
+ $e['offset'] = ftell($fh);
+
+ fwrite($fh, $zipSignature);
+ fwrite($fh, pack('s', $e['vneeded'])); // version_needed
+ fwrite($fh, pack('s', $e['bitflag'])); // general_bit_flag
+ fwrite($fh, pack('s', $e['cmethod'])); // compression_method
+ fwrite($fh, pack('s', $e['modtime'])); // lastmod_time
+ fwrite($fh, pack('s', $e['moddate'])); // lastmod_date
+ fwrite($fh, pack('V', $e['crc_32'])); // crc-32
+ fwrite($fh, pack('I', $e['comsize'])); // compressed_size
+ fwrite($fh, pack('I', $e['uncsize'])); // uncompressed_size
+ fwrite($fh, pack('s', mb_strlen($cfilename, '8bit'))); // file_name_length
+ fwrite($fh, pack('s', 0)); // extra_field_length
+ fwrite($fh, $cfilename); // file_name
+ // ignoring extra_field
+ fwrite($fh, $zdata);
+
+ // Append it to central dir
+ $e['external_attributes'] = (substr($cfilename, -1) === '/'&&!$zdata)?16:32; // Directory or file name
+ $e['comments'] = '';
+
+ $cdrec .= $dirSignature;
+ $cdrec .= "\x0\x0"; // version made by
+ $cdrec .= pack('v', $e['vneeded']); // version needed to extract
+ $cdrec .= "\x0\x0"; // general bit flag
+ $cdrec .= pack('v', $e['cmethod']); // compression method
+ $cdrec .= pack('v', $e['modtime']); // lastmod time
+ $cdrec .= pack('v', $e['moddate']); // lastmod date
+ $cdrec .= pack('V', $e['crc_32']); // crc32
+ $cdrec .= pack('V', $e['comsize']); // compressed filesize
+ $cdrec .= pack('V', $e['uncsize']); // uncompressed filesize
+ $cdrec .= pack('v', mb_strlen($cfilename,'8bit')); // file name length
+ $cdrec .= pack('v', 0); // extra field length
+ $cdrec .= pack('v', mb_strlen($e['comments'],'8bit')); // file comment length
+ $cdrec .= pack('v', 0); // disk number start
+ $cdrec .= pack('v', 0); // internal file attributes
+ $cdrec .= pack('V', $e['external_attributes']); // internal file attributes
+ $cdrec .= pack('V', $e['offset']); // relative offset of local header
+ $cdrec .= $cfilename;
+ $cdrec .= $e['comments'];
+ }
+
+ protected function _sheetToXML($idx, $template) {
+ // locale floats fr_FR 1.234,56 -> 1234.56
+ $_loc = setlocale(LC_NUMERIC, 0);
+ setlocale(LC_NUMERIC,'C');
+ $COLS = [];
+ $ROWS = [];
+ if ( count($this->sheets[$idx]['rows']) ) {
+ $COLS[] = '';
+ $CUR_ROW = 0;
+ $COL = [];
+ foreach( $this->sheets[$idx]['rows'] as $r ) {
+ $CUR_ROW++;
+ $row = '';
+ $CUR_COL = 0;
+ foreach( $r as $v ) {
+ $CUR_COL++;
+ if ( !isset($COL[ $CUR_COL ])) {
+ $COL[ $CUR_COL ] = 0;
+ }
+ if ( $v === null || $v === '' ) {
+ continue;
+ }
+
+ $cname = $this->num2name($CUR_COL) . $CUR_ROW;
+
+ $ct = $cv = null;
+ $N = $F = $A = 0;
+
+ if ( is_string($v) ) {
+
+ if ( $v[0] === "\0" ) { // RAW value as string
+ $v = substr($v,1);
+ $vl = mb_strlen( $v );
+ } else {
+ if ( strpos( $v, '<' ) !== false ) { // tags?
+ if ( strpos( $v, '' ) !== false ) {
+ $F += self::F_BOLD;
+ }
+ if ( strpos( $v, '' ) !== false ) {
+ $F += self::F_ITALIC;
+ }
+ if ( strpos( $v, '' ) !== false ) {
+ $F += self::F_UNDERLINE;
+ }
+ if ( strpos( $v, '' ) !== false ) {
+ $F += self::F_STRIKE;
+ }
+ if ( strpos( $v, '' ) !== false ) {
+ $A += self::A_LEFT;
+ }
+ if ( strpos( $v, '' ) !== false ) {
+ $A += self::A_CENTER;
+ }
+ if ( strpos( $v, '' ) !== false ) {
+ $A += self::A_RIGHT;
+ }
+ if ( preg_match( '/(.*?)<\/a>/i', $v, $m ) ) {
+ $h = explode( '#', $m[1] );
+ $this->sheets[ $idx ]['hyperlinks'][] = ['ID' => 'rId' . ( count( $this->sheets[ $idx ]['hyperlinks'] ) + 1 ), 'R' => $cname, 'H' => $h[0], 'L' => isset( $h[1] ) ? $h[1] : ''];
+ $F = self::F_HYPERLINK; // Hyperlink
+ }
+ if ( preg_match( '/(.*?)<\/a>/i', $v, $m ) ) {
+ $this->sheets[ $idx ]['hyperlinks'][] = ['ID' => 'rId' . ( count( $this->sheets[ $idx ]['hyperlinks'] ) + 1 ), 'R' => $cname, 'H' => $m[1], 'L' => ''];
+ $F = self::F_HYPERLINK; // mailto hyperlink
+ }
+ $v = strip_tags( $v );
+ } // tags
+ $vl = mb_strlen( $v );
+ if ( $v === '0' || preg_match( '/^[-+]?[1-9]\d{0,14}$/', $v ) ) { // Integer as General
+ $cv = ltrim( $v, '+' );
+ if ( $vl > 10 ) {
+ $N = self::N_INT; // [1] 0
+ }
+ } elseif ( preg_match( '/^[-+]?(0|[1-9]\d*)\.(\d+)$/', $v, $m ) ) {
+ $cv = ltrim( $v, '+' );
+ if ( strlen( $m[2] ) < 3 ) {
+ $N = self::N_DEC;
+ }
+ } elseif ( preg_match( '/^([-+]?\d+)%$/', $v, $m ) ) {
+ $cv = round( $m[1] / 100, 2 );
+ $N = self::N_PERCENT_INT; // [9] 0%
+ } elseif ( preg_match( '/^([-+]?\d+\.\d+)%$/', $v, $m ) ) {
+ $cv = round( $m[1] / 100, 4 );
+ $N = self::N_PRECENT_DEC; // [10] 0.00%
+ } elseif ( preg_match( '/^(\d\d\d\d)-(\d\d)-(\d\d)$/', $v, $m ) ) {
+ $cv = $this->date2excel( $m[1], $m[2], $m[3] );
+ $N = self::N_DATE; // [14] mm-dd-yy
+ } elseif ( preg_match( '/^(\d\d)\/(\d\d)\/(\d\d\d\d)$/', $v, $m ) ) {
+ $cv = $this->date2excel( $m[3], $m[2], $m[1] );
+ $N = self::N_DATE; // [14] mm-dd-yy
+ } elseif ( preg_match( '/^(\d\d):(\d\d):(\d\d)$/', $v, $m ) ) {
+ $cv = $this->date2excel( 0, 0, 0, $m[1], $m[2], $m[3] );
+ $N = self::N_TIME; // time
+ } elseif ( preg_match( '/^(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)$/', $v, $m ) ) {
+ $cv = $this->date2excel( $m[1], $m[2], $m[3], $m[4], $m[5], $m[6] );
+ $N = self::N_DATETIME; // [22] m/d/yy h:mm
+ } elseif ( preg_match( '/^(\d\d)\/(\d\d)\/(\d\d\d\d) (\d\d):(\d\d):(\d\d)$/', $v, $m ) ) {
+ $cv = $this->date2excel( $m[3], $m[2], $m[1], $m[4], $m[5], $m[6] );
+ $N = self::N_DATETIME; // [22] m/d/yy h:mm
+ } elseif ( preg_match( '/^[0-9+-.]+$/', $v ) ) { // Long ?
+ $A = self::A_RIGHT;
+ } elseif ( preg_match( '/^https?:\/\/\S+$/i', $v ) ) {
+ $h = explode( '#', $v );
+ $this->sheets[ $idx ]['hyperlinks'][] = ['ID' => 'rId' . ( count( $this->sheets[ $idx ]['hyperlinks'] ) + 1 ), 'R' => $cname, 'H' => $h[0], 'L' => isset( $h[1] ) ? $h[1] : ''];
+ $F = self::F_HYPERLINK; // Hyperlink
+ } elseif ( preg_match( "/^[a-zA-Z0-9_\.\-]+@([a-zA-Z0-9][a-zA-Z0-9\-]*\.)+[a-zA-Z]{2,}$/", $v ) ) {
+ $this->sheets[ $idx ]['hyperlinks'][] = ['ID' => 'rId' . ( count( $this->sheets[ $idx ]['hyperlinks'] ) + 1 ), 'R' => $cname, 'H' => 'mailto:' . $v, 'L' => ''];
+ $F = self::F_HYPERLINK; // Hyperlink
+ }
+ }
+ if ( !$cv) {
+
+ $v = $this->esc( $v );
+
+ if ( mb_strlen( $v ) > 160 ) {
+ $ct = 'inlineStr';
+ $cv = $v;
+ } else {
+ $ct = 's'; // shared string
+ $cv = false;
+ $skey = '~' . $v;
+ if ( isset( $this->SI_KEYS[ $skey ] ) ) {
+ $cv = $this->SI_KEYS[ $skey ];
+ }
+ if ( $cv === false ) {
+ $this->SI[] = $v;
+ $cv = count( $this->SI ) - 1;
+ $this->SI_KEYS[ $skey ] = $cv;
+ }
+ }
+ }
+ } elseif ( is_int( $v ) ) {
+ $vl = mb_strlen( (string) $v );
+ $cv = $v;
+ } elseif ( is_float( $v ) ) {
+ $vl = mb_strlen( (string) $v );
+ $cv = $v;
+ } elseif ( $v instanceof DateTime ) {
+ $vl = 16;
+ $cv = $this->date2excel( $v->format('Y'), $v->format('m'), $v->format('d'), $v->format('H'), $v->format('i'), $v->format('s') );
+ $N = self::N_DATETIME; // [22] m/d/yy h:mm
+ } else {
+ continue;
+ }
+
+ $COL[ $CUR_COL ] = max( $vl, $COL[ $CUR_COL ] );
+
+ $cs = 0;
+ if ( $N + $F + $A > 0 ) {
+
+ if ( isset($this->F_KEYS[ $F ] ) ) {
+ $cf = $this->F_KEYS[ $F ];
+ } else {
+ $cf = count($this->F);
+ $this->F_KEYS[$F] = $cf;
+ $this->F[] = $F;
+ }
+ $NFA = 'N' . $N . 'F' . $cf . 'A' . $A;
+ if ( isset( $this->XF_KEYS[ $NFA ] ) ) {
+ $cs = $this->XF_KEYS[ $NFA ];
+ }
+ if ( $cs === 0 ) {
+ $cs = count( $this->XF );
+ $this->XF_KEYS[ $NFA ] = $cs;
+ $this->XF[] = [$N, $cf, $A];
+ }
+ }
+
+ $row .= ''
+ .($ct === 'inlineStr' ? ''.$cv.'' : '' . $cv . '')."\r\n";
+ }
+ $ROWS[] = $row . "
\r\n";
+ }
+ foreach ( $COL as $k => $max ) {
+ $COLS[] = '';
+ }
+ $COLS[] = '';
+ $REF = 'A1:'.$this->num2name(count($COLS)) . $CUR_ROW;
+ } else {
+ $ROWS[] = '0
';
+ $REF = 'A1:A1';
+ }
+ $HYPERLINKS = [];
+ if ( count( $this->sheets[$idx]['hyperlinks']) ) {
+ $HYPERLINKS[] = '';
+ foreach ( $this->sheets[$idx]['hyperlinks'] as $h ) {
+ $HYPERLINKS[] = '';
+ }
+ $HYPERLINKS[] = '';
+ }
+ //restore locale
+ setlocale(LC_NUMERIC, $_loc);
+
+ return str_replace(['{REF}','{COLS}','{ROWS}','{HYPERLINKS}'],
+ [ $REF, implode("\r\n", $COLS), implode("\r\n",$ROWS), implode("\r\n", $HYPERLINKS) ],
+ $template );
+ }
+
+ public function num2name($num) {
+ $numeric = ($num - 1) % 26;
+ $letter = chr( 65 + $numeric );
+ $num2 = (int) ( ($num-1) / 26 );
+ if ( $num2 > 0 ) {
+ return $this->num2name( $num2 ) . $letter;
+ }
+ return $letter;
+ }
+
+ public function date2excel($year, $month, $day, $hours=0, $minutes=0, $seconds=0) {
+ $excelTime = (($hours * 3600) + ($minutes * 60) + $seconds) / 86400;
+
+ if ( $year === 0 ) {
+ return $excelTime;
+ }
+
+ // self::CALENDAR_WINDOWS_1900
+ $excel1900isLeapYear = True;
+ if (((int)$year === 1900) && ($month <= 2)) { $excel1900isLeapYear = False; }
+ $myExcelBaseDate = 2415020;
+
+ // Julian base date Adjustment
+ if ($month > 2) {
+ $month -= 3;
+ } else {
+ $month += 9;
+ --$year;
+ }
+ // Calculate the Julian Date, then subtract the Excel base date (JD 2415020 = 31-Dec-1899 Giving Excel Date of 0)
+ $century = substr($year,0,2);
+ $decade = substr($year,2,2);
+ $excelDate = floor((146097 * $century) / 4) + floor((1461 * $decade) / 4) + floor((153 * $month + 2) / 5) + $day + 1721119 - $myExcelBaseDate + $excel1900isLeapYear;
+
+ return (float) $excelDate + $excelTime;
+ }
+ public function setDefaultFont( $name ) {
+ $this->defaultFont = $name;
+ return $this;
+ }
+ public function setDefaultFontSize( $size ) {
+ $this->defaultFontSize = $size;
+ return $this;
+ }
+ public function esc( $str ) {
+ // XML UTF-8: #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
+ // but we use fast version
+ return str_replace( ['&', '<', '>', "\x00","\x03","\x0B"], ['&', '<', '>', '', '', ''], $str );
+ }
+}
diff --git a/index.php b/index.php
new file mode 100644
index 0000000..fb49f88
--- /dev/null
+++ b/index.php
@@ -0,0 +1,676 @@
+init_gateway();
+ $this->init_form_fields();
+ $this->init_settings();
+
+ $this->title = $this->get_option('title');
+ $this->description = $this->get_option('description');
+ $this->tranportal_id = $this->get_option('tranportal_id');
+ $this->password = $this->get_option('password');
+ $this->resource_key = $this->get_option('resource_key');
+ $this->is_test = $this->get_option('is_test');
+ if($this->is_test == "no")
+ {
+ $this->GatewayUrl = "https://kpay.com.kw/";
+ }
+ add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
+ add_filter('woocommerce_thankyou_order_received_text', [$this,'wc_woo_change_order_received_text'] );
+ add_filter( 'woocommerce_endpoint_order-received_title', [$this,'wc_thank_you_title']);
+
+ // add routers
+ //add_action('init', [$this,'wc_knet_rewrite_tag_rule'], 10, 0);
+ }
+
+ public function wc_woo_change_order_received_text($str) {
+ global $id;
+ $order_status = $this->get_order_in_recived_page($id);
+ return sprintf("%s %s.",__("Thank you. Your order has been","wc_knet"),$this->get_status_color($order_status),__(ucfirst($order_status),"woocommerce"));
+ }
+
+ public function wc_thank_you_title( $old_title){
+ global $id;
+ $order_status = $this->get_order_in_recived_page($id);
+
+ if ( isset ( $order_status ) ) {
+ return sprintf( "%s , %s",__('Order',"wc_knet"),$this->get_status_color($order_status), esc_html( __(ucfirst($order_status),"woocommerce")) );
+ }
+ return $old_title;
+ }
+
+ private function get_order_in_recived_page($page_id){
+ global $wp;
+ if ( is_order_received_page() && get_the_ID() === $page_id ) {
+ $order_id = apply_filters( 'woocommerce_thankyou_order_id', absint( $wp->query_vars['order-received'] ) );
+ $order_key = apply_filters( 'woocommerce_thankyou_order_key', empty( $_GET['key'] ) ? '' : wc_clean( $_GET['key'] ) );
+ if ( $order_id > 0 ) {
+ $order = new WC_Order( $order_id );
+
+ if ( $order->get_order_key() != $order_key ) {
+ $order = false;
+ }
+ return $order->get_status();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * set status color
+ * @param $status
+ * @return string
+ */
+ private function get_status_color($status){
+ switch ($status){
+ case "pending":
+ return "#0470fb";
+ case "processing":
+ return "#fbbd04";
+ case "on-hold":
+ return "#04c1fb";
+ case "completed":
+ return "green";
+ default:
+ return "#fb0404";
+ }
+ }
+
+ /**
+ * define knet route like knetresponce/success
+ */
+ /*public function wc_knet_rewrite_tag_rule() {
+ add_rewrite_rule( '^knetresponce/([^/]*)/?', 'index.php?knetresponce=$matches[1]','top' );
+ }*/
+ /**
+ * initialization gateway call default data
+ * like id,icon
+ */
+ public function init_gateway()
+ {
+ $this->id = 'wc_knet';
+ $this->icon = plugins_url( 'assets/knet-logo.png' , __FILE__ );
+ $this->method_title = __('Knet', 'wc_knet');
+ $this->method_description = __( 'intgration with knet php raw.', 'woocommerce' );
+ $this->has_fields = true;
+ }
+ /**
+ * Define Form Option fields
+ * - Options for payment like 'title', 'description', 'tranportal_id', 'password', 'resource_key'
+ **/
+ public function init_form_fields()
+ {
+ $this->form_fields = array(
+ 'enabled' => array(
+ 'title' => __( 'Enable/Disable', 'woocommerce' ),
+ 'type' => 'checkbox',
+ 'label' => __( 'Enable Knet Payment', 'woocommerce' ),
+ 'default' => 'yes'
+ ),
+ 'is_test' => array(
+ 'title' => 'Test mode',
+ 'label' => 'Enable Test Mode',
+ 'type' => 'checkbox',
+ 'description' => 'Place the payment gateway in test mode using test API keys.',
+ 'default' => 'no',
+ 'desc_tip' => true,
+ ),
+ 'title' => array(
+ 'title' => __( 'Title', 'woocommerce' ),
+ 'type' => 'text',
+ 'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ),
+ 'default' => __( 'knet', 'woocommerce' ),
+ 'desc_tip' => true,
+ ),
+ 'description' => array(
+ 'title' => __( 'Description', 'woocommerce' ),
+ 'type' => 'textarea',
+ 'default' => ''
+ ),
+ 'tranportal_id' => array(
+ 'title' => __( 'Tranportal Id', 'wc_knet' ),
+ 'type' => 'text',
+ 'label' => __( 'Necessary data requested from the bank ', 'wc_knet' ),
+ 'default' => ''
+ ),
+ 'password' => array(
+ 'title' => __( 'Transportal Password', 'wc_knet' ),
+ 'type' => 'password',
+ 'description' => __( 'Necessary data requested from the bank ', 'wc_knet' ),
+ 'default' => '',
+ 'desc_tip' => true,
+ ),
+ 'resource_key' => array(
+ 'title' => __( 'Terminal Resource Key', 'wc_knet' ),
+ 'type' => 'password',
+ 'description' => __( 'Necessary data requested from the bank', 'wc_knet' ),
+ 'default' => '',
+ 'desc_tip' => true,
+ ),
+
+ );
+ }
+ /**
+ * Admin Panel Options
+ * - Options for bits like 'title', 'description', 'alias'
+ **/
+ public function admin_options(){
+ echo '
'.__('Knet', 'wc_knet').'
';
+ echo ''.__('Knet', 'wc_knet').'
';
+ echo '';
+
+ }
+ /**
+ * Process payment
+ * return array
+ * status,pay url
+ * 1- get request data (pay url)
+ * 2- Mark as on-hold (we're awaiting the cheque)
+ * 3- Remove cart
+ * 4- Return thankyou redirect
+ * 5- or failed pay
+ */
+ function process_payment( $order_id )
+ {
+
+ global $woocommerce;
+ $order = new WC_Order( $order_id );
+ if(!$order->get_id())
+ {
+ wc_add_notice( __("Order not found", "wc_knet"), 'error' );
+ return array(
+ 'result' => 'error',
+ 'redirect' => $this->get_return_url( $order )
+ );
+ }
+ //get request data (pay url)
+ $request = $this->request($order);
+
+ // Mark as on-hold (we're awaiting the cheque)
+ $order->update_status('on-hold', __( 'Awaiting cheque payment', 'woocommerce' ));
+
+ // Remove cart
+ //$woocommerce->cart->empty_cart();
+
+ if(!empty($request))
+ {
+ // Return thankyou redirect
+ return array(
+ 'result' => 'success',
+ 'redirect' => $request['url']
+ );
+ }
+ else
+ {
+ $order->add_order_note( __('Payment error:', 'woothemes') . __("Knet can't get data", 'wc_knet'),'error' );
+ $order->update_status('failed');
+ return array(
+ 'result' => 'error',
+ 'redirect' => $this->get_return_url( $order )
+ );
+ }
+ }
+
+ /**
+ * return pay url to rediredt to knet gateway web site
+ * return array
+ */
+ public function request($order)
+ {
+ $this->formatUrlParames($order);
+ $param = $this->encryptAES($this->paymentUrl,$this->resource_key)."&tranportalId=".$this->tranportal_id."&responseURL=".$this->responseURL."&errorURL=".$this->errorURL;
+ $payURL= $this->GatewayUrl."kpg/PaymentHTTP.htm?param=paymentInit"."&trandata=".$param;
+ return [
+ 'status' => 'success',
+ 'url' =>$payURL,
+ 'payment_id' => $this->trackId
+ ];
+ }
+ /**
+ * prepare pay url parames to kent
+ * this update pay url var
+ */
+ private function formatUrlParames($order)
+ {
+ $this->user_id = $order->get_user_id();
+ if($this->user_id)
+ {
+ $user_info = $order->get_user();
+ $this->name = $user_info->user_login;
+ $this->email = $user_info->user_email;
+ $this->mobile = $user_info->user_phone;
+ }
+ $this->errorURL = get_site_url()."/index.php?knetresponce=success";
+ $this->responseURL = get_site_url()."/index.php?knetresponce=success";
+
+ $this->trackId = time().mt_rand(1000,100000);
+ $replace_array = array();
+ $replace_array['{id}'] = $this->tranportal_id;
+ $replace_array['{password}'] = $this->password;
+ $replace_array['{amt}'] = $order->get_total();
+ $replace_array['{trackid}'] = $this->trackId;
+ $replace_array['{responseURL}'] = $this->responseURL;
+ $replace_array['{errorURL}'] = $this->errorURL;
+
+ $replace_array['{udf1}'] = $order->get_id();
+ $replace_array['{udf2}'] = $this->name;
+ $replace_array['{udf3}'] =$this->email;
+ $replace_array['{udf4}'] = $this->mobile;
+ $replace_array['{udf5}'] = '';
+ $this->paymentUrl = str_replace(array_keys($replace_array),array_values($replace_array),$this->paymentUrl);
+ }
+ /**
+ * update order after responce Done from knet
+ * return string
+ * url for order view
+ */
+ public function updateOrder()
+ {
+ // defince rexpoce data
+ $resnopseData = $this->responce();
+
+ if($resnopseData)
+ {
+
+ $order_id = $resnopseData["udf1"];
+ $status = $resnopseData["status"];
+ $tranid = $resnopseData["tranid"];
+ $ref = $resnopseData["ref"];
+ $paymentid = $resnopseData["paymentid"];
+ $trackid = $resnopseData["trackid"];
+ $result = $resnopseData["result"];
+ $ErrorText = $resnopseData["ErrorText"];
+ $Error = $resnopseData["Error"];
+ $order = new WC_Order( $order_id );
+ $transation_data = [
+ "payment_id"=> $paymentid,
+ "track_id"=>$trackid,
+ "tran_id"=>$tranid,
+ "ref_id"=>$ref,
+ "result"=>$result,
+ 'status' => ($result == "CAPTURED") ? STATUS_SUCCESS : STATUS_FAIL,
+ "amount" => $resnopseData["ammount"],
+ "data" => $resnopseData["data"],
+ 'error'=>$ErrorText,
+ ];
+ if(!$order->get_id())
+ {
+ wc_add_notice( __("Order not found", "wc_knet"), 'error' );
+ return $order->get_view_order_url();
+ }
+ elseif(isset($status) && $status == "success")
+ {
+
+ switch ($result) {
+ case 'CAPTURED':
+ $order->payment_complete();
+ $order->update_status('completed');
+ break;
+ case 'NOT CAPTURED':
+ $order->update_status('refunded');
+ break;
+ case 'CANCELED':
+ $order->update_status('cancelled');
+ break;
+ default:
+ $order->update_status('refunded');
+ break;
+ }
+
+ $knetInfomation = "";
+ $knetInfomation.= __('Result', 'woothemes')." : $result\n";
+ $knetInfomation.= __('Payment id', 'woothemes')." : $paymentid\n";
+ $knetInfomation.= __('track id', 'woothemes')." : $trackid\n";
+ $knetInfomation.= __('Transaction id', 'woothemes')." : $tranid\n";
+ $knetInfomation.= __('Refrance id', 'woothemes')." : $ref\n";
+ $order->add_order_note($knetInfomation);
+ // insert transation
+ do_action("wc_knet_create_new_transation",$order,$transation_data);
+ }
+ elseif(isset($status) && $status == "error")
+ {
+ $knetInfomation = "";
+ $knetInfomation.= __('Result', 'woothemes')." : $result\n";
+ $knetInfomation.= __('Payment id', 'woothemes')." : $paymentid\n";
+ $knetInfomation.= __('track id', 'woothemes')." : $trackid\n";
+ $knetInfomation.= __('Transaction id', 'woothemes')." : $tranid\n";
+ $knetInfomation.= __('Refrance id', 'woothemes')." : $ref\n";
+ $knetInfomation.= __('Error', 'woothemes')." : $Error\n";
+ $knetInfomation.= __('Error Message', 'woothemes')." : $ErrorText\n";
+ $order->add_order_note($knetInfomation);
+ $order->update_status('refunded');
+
+ // insert transation
+ do_action("wc_knet_create_new_transation",$order,$transation_data);
+ }
+ }
+ return $this->get_return_url($order);
+ }
+ /**
+ * get responce came from kney payment
+ * return array()
+ */
+ private function responce()
+ {
+ $ResErrorText = (isset($_REQUEST['ErrorText'])) ? sanitize_text_field($_REQUEST['ErrorText']) : null; //Error Text/message
+ $ResPaymentId = (isset($_REQUEST['paymentid'])) ? sanitize_text_field($_REQUEST['paymentid']) : null; //Payment Id
+ $ResTrackID = (isset($_REQUEST['trackid'])) ? sanitize_text_field($_REQUEST['trackid']) : null; //Merchant Track ID
+ $ResErrorNo = (isset($_REQUEST['Error'])) ? sanitize_text_field($_REQUEST['Error']) : null; //Error Number
+ $ResResult = (isset($_REQUEST['result'])) ? sanitize_text_field($_REQUEST['result']) : null; //Transaction Result
+ $ResPosdate = (isset($_REQUEST['postdate'])) ? sanitize_text_field($_REQUEST['postdate']) : null; //Postdate
+ $ResTranId = (isset($_REQUEST['tranid'])) ? sanitize_text_field($_REQUEST['tranid']) : null; //Transaction ID
+ $ResAuth = (isset($_REQUEST['auth'])) ? sanitize_text_field($_REQUEST['auth']) : null; //Auth Code
+ $ResAVR = (isset($_REQUEST['avr'])) ? sanitize_text_field($_REQUEST['avr']) : null; //TRANSACTION avr
+ $ResRef = (isset($_REQUEST['ref'])) ? sanitize_text_field($_REQUEST['ref']) : null; //Reference Number also called Seq Number
+ $ResAmount = (isset($_REQUEST['amt'])) ? sanitize_text_field($_REQUEST['amt']) : null; //Transaction Amount
+ $Resudf1 = (isset($_REQUEST['udf1'])) ? sanitize_text_field($_REQUEST['udf1']) : null; //UDF1
+ $Resudf2 = (isset($_REQUEST['udf2'])) ? sanitize_text_field($_REQUEST['udf2']) : null; //UDF2
+ $Resudf3 = (isset($_REQUEST['udf3'])) ? sanitize_text_field($_REQUEST['udf3']) : null; //UDF3
+ $Resudf4 = (isset($_REQUEST['udf4'])) ? sanitize_text_field($_REQUEST['udf4']) : null; //UDF4
+ $Resudf5 = (isset($_REQUEST['udf5'])) ? sanitize_text_field($_REQUEST['udf5']) : null; //UDF5
+ if($ResErrorText==null && $ResErrorNo==null && $ResPaymentId != null)
+ {
+ // success
+ $ResTranData= (isset($_REQUEST['trandata'])) ? sanitize_text_field($_REQUEST['trandata']) : null;
+ $decrytedData=$this->decrypt($ResTranData,$this->resource_key);
+ parse_str($decrytedData, $output);
+
+ if($ResTranData !=null)
+ {
+ $result['status'] = 'success';
+ $result['paymentid'] = $ResPaymentId;
+ $result['trackid'] = $ResTrackID;
+ $result['tranid'] = $output['tranid'];
+ $result['ref'] = $output['ref'];
+ $result['result'] = $output['result'];
+ $result['postdate'] = $output['postdate'];
+ $result['auth'] = $output['auth'];
+ $result['avr'] = $output['avr']; //TRANSACTION avr
+ $result['ammount'] = $output['amt']; //Transaction Amount
+ $result['udf1'] = $output['udf1']; //UDF1
+ $result['udf2'] = $output['udf2']; //UDF2
+ $result['udf3'] = $output['udf3']; //UDF3
+ $result['udf4'] = $output['udf4']; //UDF4
+ $result['udf5'] = $output['udf5'];
+ //Decryption logice starts
+ $result['data']=$decrytedData;
+ $result['ErrorText']= $ResErrorText; //Error
+ $result['Error'] = $ResErrorNo;
+ }else{
+ $result['status'] = 'error';
+ $result['paymentid'] = $ResPaymentId;
+ $result['trackid'] = $ResTrackID;
+ $result['tranid'] = $ResTranId;
+ $result['ref'] = $ResRef;
+ $result['result'] = 'error';
+ $result['data']= sanitize_text_field(http_build_query($_REQUEST));
+ $result['postdate'] = $ResPosdate;
+ $result['auth'] = $ResAuth;
+ $result['avr'] = $ResAVR; //TRANSACTION avr
+ $result['ammount'] = $ResAmount; //Transaction Amount
+ $result['udf1'] = $Resudf1; //UDF1
+ $result['udf2'] = $Resudf2; //UDF2
+ $result['udf3'] = $Resudf3; //UDF3
+ $result['udf4'] = $Resudf4; //UDF4
+ $result['udf5'] = $Resudf5;
+ $result['ErrorText']= $ResErrorText; //Error
+ $result['Error'] = $ResErrorNo;
+ }
+
+ }
+ else
+ {
+ // error
+ $result['status'] = 'error';
+ $result['paymentid'] = $ResPaymentId;
+ $result['trackid'] = $ResTrackID;
+ $result['tranid'] = $ResTranId;
+ $result['ref'] = $ResRef;
+ $result['result'] = 'error';
+ $result['data']= sanitize_text_field(http_build_query($_REQUEST));
+ $result['ErrorText']= $ResErrorText; //Error
+ $result['Error'] = $ResErrorNo; //Error Number
+ $result['postdate'] = $ResPosdate ; //Postdate
+ $result['auth'] = $ResAuth; //Auth Code
+ $result['avr'] = $ResAVR; //TRANSACTION avr
+ $result['ammount'] = $ResAmount; //Transaction Amount
+ $result['udf1'] = $Resudf1; //UDF1
+ $result['udf2'] = $Resudf2; //UDF2
+ $result['udf3'] = $Resudf3; //UDF3
+ $result['udf4'] = $Resudf4; //UDF4
+ $result['udf5'] = $Resudf5;
+ }
+
+ //UDF5
+ return $result;
+ }
+ /** ======== Payment Encrypt Functions Started ======
+ * this functions created by knet devolper don't change any thing
+ */
+ public function encryptAES($str,$key)
+ {
+ $str = $this->pkcs5_pad($str);
+ $encrypted = openssl_encrypt($str, 'AES-128-CBC', $key, OPENSSL_ZERO_PADDING, $key);
+ $encrypted = base64_decode($encrypted);
+ $encrypted=unpack('C*', ($encrypted));
+ $encrypted=$this->byteArray2Hex($encrypted);
+ $encrypted = urlencode($encrypted);
+ return $encrypted;
+ }
+
+ public function pkcs5_pad ($text)
+ {
+ $blocksize = 16;
+ $pad = $blocksize - (strlen($text) % $blocksize);
+ return $text . str_repeat(chr($pad), $pad);
+ }
+ public function byteArray2Hex($byteArray)
+ {
+ $chars = array_map("chr", $byteArray);
+ $bin = join($chars);
+ return bin2hex($bin);
+ }
+
+ public function decrypt($code,$key)
+ {
+ $code = $this->hex2ByteArray(trim($code));
+ $code=$this->byteArray2String($code);
+ $iv = $key;
+ $code = base64_encode($code);
+ $decrypted = openssl_decrypt($code, 'AES-128-CBC', $key, OPENSSL_ZERO_PADDING, $iv);
+ return $this->pkcs5_unpad($decrypted);
+ }
+
+ public function hex2ByteArray($hexString)
+ {
+ $string = hex2bin($hexString);
+ return unpack('C*', $string);
+ }
+
+
+ public function byteArray2String($byteArray)
+ {
+ $chars = array_map("chr", $byteArray);
+ return join($chars);
+ }
+
+ public function pkcs5_unpad($text)
+ {
+ $pad = ord($text{strlen($text)-1});
+ if ($pad > strlen($text)) {
+ return false;
+ }
+ if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) {
+ return false;
+ }
+ return substr($text, 0, -1 * $pad);
+ }
+ /** ======== Payment Encrypt Functions Ended ====== */
+
+ }
+
+ }
+
+ /**
+ * Add the Gateway to WooCommerce
+ **/
+ function woocommerce_add_wc_knet_gateway($methods) {
+ global $WC_KNET_CLASS_NAME;
+ $methods[] = $WC_KNET_CLASS_NAME;
+ return $methods;
+ }
+ add_filter('woocommerce_payment_gateways', 'woocommerce_add_wc_knet_gateway' );
+
+ /**
+ * load plugin language
+ * @param $mofile
+ * @param $domain
+ * @return string
+ */
+ function wc_knet_load_textdomain() {
+ load_plugin_textdomain( 'wc_knet', false, basename( dirname( __FILE__ ) ) . '/languages/' );
+ }
+ add_action( 'plugins_loaded', 'wc_knet_load_textdomain' );
+
+
+ /**
+ * add knet responce query var
+ */
+ add_filter( 'query_vars', function( $query_vars ) {
+ $query_vars[] = 'knetresponce';
+ $query_vars[] = 'wc_knet_export';
+ return $query_vars;
+ } );
+ /**
+ * define knet responce
+ */
+ add_action("wp",function($request)
+ {
+
+ if( isset($request->query_vars['knetresponce']) && null !== sanitize_text_field($request->query_vars['knetresponce']) && sanitize_text_field($request->query_vars['knetresponce']) == "success")
+ {
+ $WC_Gateway_Knet = new WC_Gateway_Knet();
+ $url = $WC_Gateway_Knet->updateOrder();
+
+ if ( wp_redirect( $url ) )
+ {
+ exit;
+ }
+ }
+
+ });
+
+ add_action("admin_init",function ($request){
+ $action = esc_attr($_GET["wc_knet_export"] ?? "");
+ if(is_admin()){
+ if(sanitize_text_field($action) == "excel"){
+ $rows = wc_knet_trans_grid::get_transations(1000);
+ $list[] =[__('Order', "wc_knet"), __('Status', "wc_knet"), __('Result', "wc_knet"), __('Amount', "wc_knet"), __('Payment id', "wc_knet"), __('Tracking id', "wc_knet"), __('Transaction id', "wc_knet"), __('Refrance id', "wc_knet"), __('Created at', "wc_knet") ];
+ if($rows){
+ foreach ($rows as $row){
+ $list[] = [$row['order_id'],__($row['status'],"wc_kent"),$row['result'],$row['amount'],$row['payment_id'],$row['track_id'],$row['tran_id'],$row['ref_id'],$row['created_at']];
+ }
+ }
+ $xlsx = SimpleXLSXGen::fromArray( $list );
+ $xlsx->downloadAs(date("YmdHis").'.xlsx'); // or downloadAs('books.xlsx') or $xlsx_content = (string) $xlsx
+ exit();
+ }elseif (sanitize_text_field($action) == "csv"){
+
+ $rows = wc_knet_trans_grid::get_transations(1000);
+ if($rows){
+ $filename = date('YmdHis') . ".csv";
+ $f = fopen('php://memory', 'w');
+ $delimiter = ",";
+ $head = [__('Order', "wc_knet"), __('Status', "wc_knet"), __('Result', "wc_knet"), __('Amount', "wc_knet"), __('Payment id', "wc_knet"), __('Tracking id', "wc_knet"), __('Transaction id', "wc_knet"), __('Refrance id', "wc_knet"), __('Created at', "wc_knet") ];
+ fputcsv($f, $head,$delimiter);
+ foreach ($rows as $row){
+ $listData = [$row['order_id'],__($row['status'],"wc_kent"),$row['result'],$row['amount'],$row['payment_id'],$row['track_id'],$row['tran_id'],$row['ref_id'],$row['created_at']];
+ fputcsv($f, $listData, $delimiter);
+ }
+ fseek($f, 0);
+ header('Content-Type: text/csv');
+ header('Content-Disposition: attachment; filename="' . $filename . '";');
+ fpassthru($f);
+ exit();
+ }
+ }
+ }
+
+ });
+ // call to install data
+ register_activation_hook( __FILE__, 'create_transactions_db_table' );
+
+ /**
+ * notify is currency not KWD
+ */
+ add_action('admin_notices', 'wc_knet_is_curnancy_not_kwd');
+ function wc_knet_is_curnancy_not_kwd(){
+ $currency = get_option('woocommerce_currency');
+ if(isset($currency) && $currency != "KWD"){
+ echo '
+
'.__("currency must be KWD when using this knet payment","wc_knet").'
+
';
+ }
+ }
+
+?>
\ No newline at end of file
diff --git a/languages/wc_knet-ar.mo b/languages/wc_knet-ar.mo
new file mode 100644
index 0000000..b475f79
--- /dev/null
+++ b/languages/wc_knet-ar.mo
@@ -0,0 +1,112 @@
+#: index.php:67
+msgid "Knet"
+msgstr "كي نت"
+
+#: index.php:99
+msgid "Tranportal Id"
+msgstr "Tranportal Id"
+
+#: index.php:101
+msgid "Necessary data requested from the bank "
+msgstr "بيانات ضرورية تطلب من البنك"
+
+#: index.php:105
+msgid "Transportal Password"
+msgstr ""
+
+#: index.php:107
+msgid "Necessary data requested from the bank "
+msgstr "بيانات ضرورية تطلب من البنك"
+
+#: index.php:112
+msgid "Terminal Resource Key"
+msgstr ""
+
+#: index.php:114
+msgid "Necessary data requested from the bank "
+msgstr "بيانات ضرورية تطلب من البنك"
+
+#: index.php:126
+msgid "Knet"
+msgstr ""
+
+#: index.php:167
+msgid "Knet can't get data"
+msgstr ""
+
+#: index.php:281
+msgid "Order not found"
+msgstr "لم يتم العثور علي الطب"
+
+#: index.php:82
+msgid "Thank you. Your order has been"
+msgstr "شكرا لك حالة طلبك هي :"
+
+#: index.php:90
+msgid "Order"
+msgstr "طلبك "
+
+#: index.php:629
+msgid "currency must be KWD when using this knet payment"
+msgstr "العملة يجب ان تكون بالدينار الكويت KWD عندما تحدد الكي نت كخيار للدفع "
+
+
+msgid "fail"
+msgstr "فشلت"
+
+msgid "success"
+msgstr "نجحت"
+
+msgid "Knet transactions"
+msgstr "عمليت الكي نت"
+
+msgid "Filter"
+msgstr "تصفية"
+
+msgid "WC_Knet_List"
+msgstr ""
+
+msgid "Order"
+msgstr "رقم الطلب"
+
+msgid "Status"
+msgstr "الحالة"
+
+msgid "Result"
+msgstr "نتيجة الدفع"
+
+msgid "Amount"
+msgstr "المبلغ"
+
+msgid "Payment id"
+msgstr ""
+
+msgid "Tracking id"
+msgstr ""
+
+msgid "Transaction id"
+msgstr ""
+
+msgid "Refrance id"
+msgstr ""
+
+msgid "Created at"
+msgstr "تمت بتاريخ"
+
+msgid "Reset"
+msgstr "اعادة تهيئة"
+
+msgid "Export excel"
+msgstr "تصدير الي اكسل"
+
+msgid "Export csv"
+msgstr "تصدير ال csv"
+
+msgid "No Transations avaliable."
+msgstr "لم يتم العثور علي عمليات دفع"
+
+
+
+
+
+
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..eecf835
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,58 @@
+=== Payment Gateway for knet on WooCommerce ===
+Contributors: hassan
+Tags: K-Net, knet, knetv2, payment, kuwait, woocommerce, ecommerce, payment, gateway
+Requires at least: 5.6
+Tested up to: 5.8
+Tested in WooCommerce : 5.8
+Requires PHP: 7.0
+Stable tag: 2.0.0
+License: MIT
+License URI: https://choosealicense.com/licenses/mit/
+
+نساعدك في تطوير اعمالك الخاصه بتقديم الاضافة الجديد
+الخاصة بالدفع عن طريق بوابة الكي نت بعد تحديثها
+وسع دائرة عملائك باتاحة امكانية الدفع عن طريق الكي نت
+==========
+We help you to develop your business by introducing the new add-on
+For payment through the K-Net portal, after it has been updated
+Expand your customers' circle by making the payment available via Knet
+
+## Installation
+
+download and unzip to plugins folder
+
+or
+From merchant’s WordPress admin
+1. Go to plugin section-> Add new
+2. Search for “Payment Gateway for knet on WooCommerce”
+3. Click on Install Now
+4. Click on Activate
+
+
+## Usage
+
+go to woocommerce setting in side menu and select tab payment and active knet v2 from list
+
+
+## Changelog
+
+== Changelog ==
+
+= 2.0.0 =
+* add knet transations list for order
+* export transations to csv,excel
+* change licenses to mit
+
+= 1.1.0 =
+* add order status in received page.
+* colored status
+- ![#0470fb](https://via.placeholder.com/15/0470fb/000000?text=+) `pending`
+- ![#fbbd04](https://via.placeholder.com/15/fbbd04/000000?text=+) `processing`
+- ![#04c1fb](https://via.placeholder.com/15/0470fb/000000?text=+) `on-hold`
+- ![#green](https://via.placeholder.com/15/green/000000?text=+) `completed`
+- ![#fb0404](https://via.placeholder.com/15/fb0404/000000?text=+) `cancelled,refunded,failed`
+
+
+## License
+
+[MIT](https://choosealicense.com/licenses/mit/)
\ No newline at end of file
diff --git a/transactions.php b/transactions.php
new file mode 100644
index 0000000..f39c859
--- /dev/null
+++ b/transactions.php
@@ -0,0 +1,62 @@
+get_charset_collate();
+ $table_name = $wpdb->prefix.WC_KNET_TABLE;
+ $sql = "CREATE TABLE IF NOT EXISTS $table_name (
+ id int(11) NOT NULL AUTO_INCREMENT,
+ order_id int(11) NOT NULL,
+ payment_id varchar(255) NOT NULL,
+ track_id varchar(255) NOT NULL,
+ amount DECIMAL(20,3) DEFAULT 0.000 NOT NULL,
+ tran_id varchar(255) NULL,
+ ref_id varchar(255) NULL,
+ status varchar(255) DEFAULT '".STATUS_FAIL."' NOT NULL,
+ result varchar(255) DEFAULT '".STATUS_NEW."' NOT NULL,
+ info text NULL,
+ created_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ PRIMARY KEY (id),
+ INDEX (id, order_id, payment_id, result)
+ ) $charset_collate;";
+
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
+ dbDelta( $sql );
+ add_option( 'wc_knet_db_version', WC_KNET_DV_VERSION);
+}
+
+/**
+ * create new transation record
+ * @param $data
+ * @return bool|false|int
+ */
+add_action("wc_knet_create_new_transation","fun_wc_knet_create_new_transation", 10, 2);
+function fun_wc_knet_create_new_transation($order,$transation_data){
+ global $wpdb;
+ $table_name = $wpdb->prefix.WC_KNET_TABLE;
+ try {
+ return $wpdb->insert(
+ $table_name,
+ [
+ 'order_id' => $order->id,
+ 'payment_id' => $transation_data["payment_id"],
+ 'track_id' => $transation_data["track_id"],
+ 'tran_id' => $transation_data["tran_id"],
+ 'ref_id' => $transation_data["ref_id"],
+ 'status' => $transation_data["status"],
+ 'result' => $transation_data["result"],
+ 'amount'=>$transation_data["amount"],
+ 'info' => json_encode($transation_data),
+ 'created_at' => date("Y-m-d H:i:s"),
+ ]
+ );
+ }catch (Exception $e){
+ return false;
+ }
+}
+
diff --git a/wc_knet_trans_grid.php b/wc_knet_trans_grid.php
new file mode 100644
index 0000000..46acfd8
--- /dev/null
+++ b/wc_knet_trans_grid.php
@@ -0,0 +1,350 @@
+ __( 'WC_Knet_List', "wc_knet" ), //singular name of the listed records
+ 'plural' => __( 'WC_Knet_List', "wc_knet" ), //plural name of the listed records
+ 'ajax' => false //should this table support ajax?
+
+ ]);
+ $this->db = $wpdb;
+ $this->table = $this->db->prefix.WC_KNET_TABLE;
+ }
+
+
+ private function filter_query(){
+ $query = "";
+ if(isset($_REQUEST["order_id"]) && !empty($_REQUEST["order_id"])){
+ $order_id = sanitize_text_field($_REQUEST["order_id"]);
+ $query .=" AND `order_id` = $order_id";
+ }
+ if(isset($_REQUEST["status"]) && !empty($_REQUEST["status"])){
+ $status = sanitize_text_field($_REQUEST["status"]);
+ $query .=" AND `status` = '$status'";
+ }
+ if(isset($_REQUEST["result"]) && !empty($_REQUEST["result"])){
+ $result = sanitize_text_field($_REQUEST["result"]);
+ $query .=" AND `result` LIKE '%$result%'";
+ }
+ if(isset($_REQUEST["amount"]) && !empty($_REQUEST["amount"])){
+ $amount = sanitize_text_field($_REQUEST["amount"]);
+ $query .=" AND `amount` = $amount";
+ }
+ if(isset($_REQUEST["payment_id"]) && !empty($_REQUEST["payment_id"])){
+ $payment_id = sanitize_text_field($_REQUEST["payment_id"]);
+ $query .=" AND `payment_id` = $payment_id";
+ }
+ if(isset($_REQUEST["track_id"]) && !empty($_REQUEST["track_id"])){
+ $track_id = sanitize_text_field($_REQUEST["track_id"]);
+ $query .=" AND `track_id` = $track_id";
+ }
+ if(isset($_REQUEST["tran_id"]) && !empty($_REQUEST["tran_id"])){
+ $tran_id = sanitize_text_field($_REQUEST["tran_id"]);
+ $query .=" AND `tran_id` = $tran_id";
+ }
+ if(isset($_REQUEST["ref_id"]) && !empty($_REQUEST["ref_id"])){
+ $ref_id = sanitize_text_field($_REQUEST["ref_id"]);
+ $query .=" AND `ref_id` = $ref_id";
+ }
+ if(isset($_REQUEST["created_at"]) && !empty($_REQUEST["created_at"])){
+ $created_at = sanitize_text_field($_REQUEST["created_at"]);
+ $query .=" AND `created_at` LIKE '%$created_at%'";
+ }
+
+ return $query;
+ }
+
+ public function search_box( $text, $input_id ) {
+ if ( empty( $_REQUEST['s'] ) && ! $this->has_items() ) {
+ // return;
+ }
+
+ $input_id = $input_id . '-search-input';
+
+
+ ?>
+
+
+
+
+ __('Order', "wc_knet"),
+ 'status'=>__('Status', "wc_knet"),
+ 'result'=>__('Result', "wc_knet"),
+ 'amount'=>__('Amount', "wc_knet"),
+ 'payment_id'=>__('Payment id', "wc_knet"),
+ 'track_id'=>__('Tracking id', "wc_knet"),
+ 'tran_id'=>__('Transaction id', "wc_knet"),
+ 'ref_id'=>__('Refrance id', "wc_knet"),
+ 'created_at'=>__('Created at', "wc_knet"),
+ );
+ }
+
+ public function get_sortable_columns() {
+ return $sortable = array(
+ 'order_id'=>'order_id',
+ 'status'=>'status',
+ 'result'=>'result',
+ 'payment_id'=>'payment_id',
+ 'track_id'=>'track_id',
+ 'amount'=>'amount',
+ 'tran_id'=>'tran_id',
+ 'ref_id'=>'ref_id',
+ 'created_at'=>'created_at',
+ );
+ }
+ /**
+ * Render a column when no column specific method exists.
+ *
+ * @param array $item
+ * @param string $column_name
+ *
+ * @return mixed
+ */
+ public function column_default( $item, $column_name ) {
+ switch ( $column_name ) {
+ case 'order_id':
+ return sprintf("#%s",get_edit_post_link($item[$column_name]),$item[$column_name]);
+ case 'status':
+ return ($item[$column_name] == "fail") ? sprintf("%s",__($item[ $column_name ],"wc_knet")) : sprintf("%s",__($item[ $column_name ],"wc_knet"));
+ case 'result':
+ return ($item[$column_name] != "CAPTURED") ? sprintf("%s",$item[ $column_name ]) : sprintf("%s",$item[ $column_name ]);
+ default:
+ return $item["$column_name"]; //Show the whole array for troubleshooting purposes
+ }
+ }
+ /**
+ * Handles data query and filter, sorting, and pagination.
+ */
+ public function prepare_items() {
+
+ $this->_column_headers = $this->get_column_info();
+
+ /** Process bulk action */
+ $this->process_bulk_action();
+
+ $per_page = $this->get_items_per_page( 'trans_per_page', 5 );
+ $current_page = $this->get_pagenum();
+ $total_items = self::record_count();
+
+ $this->set_pagination_args( [
+ 'total_items' => $total_items, //WE have to calculate the total number of items
+ 'per_page' => $per_page //WE have to determine how many items to show on a page
+ ] );
+
+ $this->items = self::get_transations( $per_page = 5, $page_number = 1 );
+ }
+ /**
+ * Returns the count of records in the database.
+ *
+ * @return null|string
+ */
+ public static function record_count() {
+ $sql = "SELECT COUNT(*) FROM ".(new self)->table." WHERE 1=1 ".(new self)->filter_query();
+ return (new self)->db->get_var( $sql );
+ }
+ public static function get_transations($per_page, $page_number = 1)
+ {
+
+ $sql = "SELECT * FROM ".(new self)->table." WHERE 1=1 ".(new self)->filter_query();
+
+ if ( ! empty( $_REQUEST['orderby'] ) ) {
+ $sql .= ' ORDER BY ' . esc_sql( $_REQUEST['orderby'] );
+ $sql .= ! empty( $_REQUEST['order'] ) ? ' ' . esc_sql( $_REQUEST['order'] ) : ' ASC';
+ }
+
+ $sql .= " LIMIT $per_page";
+
+ $sql .= ' OFFSET ' . ( $page_number - 1 ) * $per_page;
+
+ $result = (new self)->db->get_results( $sql, 'ARRAY_A' );
+
+ return $result;
+ }
+}
+
+class WC_KNET_Plugin
+{
+
+// class instance
+ static $instance;
+
+// customer WP_List_Table object
+ public $transations_obj;
+
+// class constructor
+ public function __construct()
+ {
+ add_filter('set-screen-option', [__CLASS__, 'set_screen'], 10, 3);
+ add_action('admin_menu', [$this, 'plugin_menu']);
+
+ }
+ public static function set_screen( $status, $option, $value ) {
+ return $value;
+ }
+ /** Singleton instance */
+ public static function get_instance() {
+ if ( ! isset( self::$instance ) ) {
+ self::$instance = new self();
+ }
+
+ return self::$instance;
+ }
+ /**
+ * Screen options
+ */
+ public function screen_option() {
+
+ $option = 'per_page';
+ $args = [
+ 'label' => 'Transations count',
+ 'default' => 5,
+ 'option' => 'trans_per_page'
+ ];
+
+ add_screen_option( $option, $args );
+
+ $this->transations_obj = new wc_knet_trans_grid();
+ }
+ public function plugin_menu() {
+
+ $hook =add_submenu_page(
+ 'woocommerce',
+ __( 'Knet transactions', 'wc_knet' ),
+ __( 'Knet transactions', 'wc_knet' ),
+ 'manage_woocommerce',
+ "wc-knet-transactions",
+ [ $this, 'plugin_settings_page' ],
+ 6
+ );
+ add_action( "load-$hook", [ $this, 'screen_option' ] );
+
+
+ }
+ /**
+ * Plugin settings page
+ */
+ public function plugin_settings_page() {
+ ?>
+
+
+
+
+
+
+
+ transations_obj->search_box(_e( 'Filter', 'wc_knet' ), "wc_knet_filter") ?>
+
+
+
+