<?php if (!defined('PmWiki')) exit();
/*
    phAttachman.php: shows attaches as a rich sortable table, can work via AJAX
    Written by (c) Philip Kazakov aka Finar (www.ph-ph.ru)
  
    This text is written for PmWiki; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published
    by the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
  
    ToDo:
    * make correct multilingual support
    * add support for audio and video files markup, using 'ImgsExention' as example. 
    * make delete operation logging (in AllRecentChanges for example)
*/

$RecipeInfo['Attachman']['Version'] = '20200522';
$RecipeInfo['Attachman']['Developer'] = 'PhPh';

SDV($HandleActions['phAttachman'], 'HandlePhAttachman');
SDV($HandleAuth['phAttachman'], 'edit'); // this would be put in '$auth' later;
Markup('attachman', '<block', '/\\(:attachman\\s*(.*?):\\)/i', "HandlePhAttachman");
 
// CONFIG: if needed, this should be redefined in config.php
SDVA($phAttachman, array(
  'EnableDelete' => false,
  'TableClass' => "sortable simpletable", // default table class
  'ImgsExentions' => "gif,jpg,jpeg,png,svg,svgz",
  'MaxImgsCountAutoPreview' => 10, // how much images will be previewed automatically
  'MaxImgSizeAutoPreview' => 1024, // max image size (KB) for be previewed automatically
  'IgnoredFilenamesPatterns' => array( // filenames to be ignored (regex patterns)
    "th[0-9]{2}---", // this is for Mini (https://www.pmwiki.org/wiki/Cookbook/Mini)
    ),
  'TextareaInsertCommands' => array (
      '*' => 'Attach:%filename%', // makes no effect, just for logic clearing
      // 'gif' => 'Mini:%filename%'; // example
    ),
  'TextareaInsertTemplate' => "<pre>%TextareaInsertCommand%</pre>", // if redefined, keep %TextareaInsertCommand% intact

  ));


function HandlePhAttachman($HandleActions_pagename, $auth = 0){
// TRANSLATIONS (todo: completely remake using core principles)
  $msg_noPreview = "Too big file, or too much images attached: click to load preview.";
  $msg_noUploads = "There are no uploads yet.";
  $msgErr_UploadsDisabled = "AttachManager Error: uploads are not enabled in config.php.";
  $msgErr_noPermission = "AttachManager Error: current user has no permisson to handle 'Attachman' cook.";
  $msgErr_noPermissionDelete = "AttachManager Error: current user has no 'upload' permisson, so 'delete' is also unaviable.";
  $msgErr_noFileToDelete = "AttachManager Delete Error: file was not found.";
  $msgErr_DeleteFailure = "AttachManager Delete Error: could't delete file, check filesystem permissions.";
  $TH_File = "File";
  $TH_Markup = "Markup";
  $TH_Size = "Size (KB)";
  $TH_Modification = "Modified";
  $TH_Action = "Action";
  
// SOME DIRTY CODE
  // We need to use all this throught ?action=phAttachman (ajax) AND throught (:attachman:) page command in the same time using one codebase.
  // I don't know how to do it correct. First $HandleActions was done, serving ?action=phAttachman.
  // After this (:attachman:) command was binded to the same function 'HandlePhAttachman'.
  // That's why in the next condition some variables are redefined manually for second case only.
  if (is_array ($HandleActions_pagename)){ // it looks like we are working with (:attachman:) syntax
    $markupCall = true;
    global $pagename; $HandleActions_pagename = $pagename;
    global $HandleAuth; $auth = $HandleAuth['phAttachman'];
    }
  
// CHECKS: 
  // check if uploads are enabled
  global $EnableUpload;
  if ( !IsEnabled($EnableUpload,0)) $stop = "$msgErr_UploadsDisabled";

  // check if current user can edit pages
  $page = RetrieveAuthPage($HandleActions_pagename, $auth, 0, READPAGE_CURRENT); 
  if (!$page) $stop = "$msgErr_noPermission";
  
  // exit() for ajax, return for (:attachman:)
  if ($stop) {if ($markupCall) return ("$stop"); else exit("$stop"); }


// PREPARE
  global $phAttachman;
  $EnableDelete = $phAttachman['EnableDelete'];
  $TableClass = $phAttachman['TableClass'];
  $ImgsExentions = $phAttachman['ImgsExentions'];
  $MaxImgsCountAutoPreview = $phAttachman['MaxImgsCountAutoPreview'];
  $MaxImgSizeAutoPreview = $phAttachman['MaxImgSizeAutoPreview'];
  $IgnoredFilenamesPatterns = $phAttachman['IgnoredFilenamesPatterns'];
  $TextareaInsertCommands = $phAttachman['TextareaInsertCommands'];
  $TextareaInsertTemplate = $phAttachman['TextareaInsertTemplate'];
  
// PROCESSING: scan and generate the table, delete files
  global $UploadDir, $UploadPrefixFmt, $UploadUrlFmt, $EnableDirectDownload;

  $uploadsFolder = FmtPageName("$UploadDir$UploadPrefixFmt", $HandleActions_pagename);
  $uploadsUrl = FmtPageName(IsEnabled($EnableDirectDownload, 1) // that's for PmiWiki inside subfolder, with EnableDirectDownload = 0
                          ? "$UploadUrlFmt$UploadPrefixFmt/"
                          : "\$PageUrl?action=download&amp;upname=",
                      $HandleActions_pagename);
  
  $filelist = @array_diff(scandir($uploadsFolder), array('.', '..'));
  if (!$filelist) {if ($markupCall) return ("$msg_noUploads"); else exit("$msg_noUploads"); }
  
  // serve 'delete' action. Works only if called throuhgt ?action=phAttachman AND user has 'upload' permissions
  if (!$markupCall && $_REQUEST["deletefile"]){
      $deletefile = "$uploadsFolder/".$_REQUEST["deletefile"];  
      $page = RetrieveAuthPage($HandleActions_pagename, "upload", 0, READPAGE_CURRENT); 
        if (!$page) exit ("$msgErr_noPermissionDelete");
      if (!is_file($deletefile)) exit($msgErr_noFileToDelete);
      if (!unlink($deletefile)) exit($msgErr_DeleteFailure);
      exit("1");
    }
  
  // CSS-styles and JS-scripts
  global $HTMLFooterFmt; // must use Footer, as Header is already generated at that point
  $HTMLFooterFmt['styles']['phAttachman'] = "
  <!-- Attachman: styles -->
    <style>
    #attachman {}
    #attachman a.phAttachman_imgLoad {
        background-color: #DDD; 
        display: block; padding: 30px 0; text-align: center;
        text-decoration: none;}
    #attachman a.phAttachman_imgLoad:hover {opacity: 0.7;}
    #attachman a.phAttachman_imgLoad span {border-bottom: 1px dotted blue;}
    #attachman td.phAttachman_size {text-align: center;}
    #attachman td.phAttachman_date {text-align: center;}
    #attachman td.phAttachman_action a {padding: 0 5px;}
    #attachman td.phAttachman_action a:hover {opacity: 0.7;}
    #attachman td.phAttachman_action span {
        display: none;
        position: fixed; left:0; top: 0px; 
        background-color: red; color: white;
        text-align: center;
        width: 100%;
        }
    #attachman td.phAttachman_action:hover span {display: block;}
    </style>
  ";
  $HTMLFooterFmt['scripts']['phAttachman'] = "
  <!-- Attachman: scripts -->
    <script>
     function imageClick(link){
        var href = link.getAttribute('href');
        var linkText = link.innerHTML;

        var image = document.createElement('img');
        
        image.src = href;
        image.setAttribute('width', '150');
        
        var linebreak = document.createElement('br');
        var text = document.createElement('small');
        text.innerHTML = linkText;
        
        link.parentNode.insertBefore(text, link.nextSibling);
        link.parentNode.insertBefore(linebreak, link.nextSibling);
        link.parentNode.insertBefore(image, link.nextSibling);
        
        link.parentNode.removeChild(link);
        
        return false;
      }
      
    function deleteRow(el){
      const request = new XMLHttpRequest();
      const url = el.getAttribute('href');
      request.open('GET', url);
      request.setRequestHeader('Content-Type', 'application/x-www-form-url');
      request.addEventListener('readystatechange', () => {
        if (request.readyState === 4 && request.status === 200) {
          if(request.responseText=='1'){
            el.parentNode.parentNode.parentNode.removeChild(el.parentNode.parentNode);
          } else {
          var text = document.createElement('span');
          text.innerHTML = request.responseText;
          el.parentNode.insertBefore(text, el.nextSibling);;
          } 
        }
    });

    request.send();
    }
    </script>
  <!-- //Attachman -->
  ";

  
  // create table
  foreach ($filelist as $key => $value) { // IgnoredFilenamesPatterns check
    foreach ($IgnoredFilenamesPatterns as $k => $v) {
      if (preg_match("/$v/",$value)) unset($filelist["$key"]);
    }
  }

  $result = "<table class='$TableClass' id='attachman'>
  <tr>
  <th>$TH_File</th>
  <th>$TH_Markup</th>
  <th>$TH_Size</th>
  <th>$TH_Modification</th>
  ".($EnableDelete ? "<th>$TH_Action</th>" : NULL)."
  </tr>";
  
  foreach ($filelist as $key => $value) { // main table generation 
    
    $FileExt = strtolower(pathinfo($value, PATHINFO_EXTENSION)); // get extension
    if (strpos($ImgsExentions, $FileExt) !== false) $FileType = 'image';
      // here support for audio and video files could be added the same way
    
    if ($FileType == 'image') {
        if (round(filesize("$uploadsFolder/$value")/1024) < $MaxImgSizeAutoPreview && $MaxImgsCountAutoPreview){
          $File = "<img src='$uploadsUrl$value' width=150px><br><small>$value</small>";
          $MaxImgsCountAutoPreview--;
        } else $File = "<a class='phAttachman_imgLoad' href='$uploadsUrl$value' title='$msg_noPreview' onclick='imageClick(this); return false;'><span>$value</span></a>";
      }
      else $File = "<a href='$uploadsUrl$value' target='_blank'>$value</a>";
    
    $Markup = "";
    if ($TextareaInsertCommands[$FileExt]) {
        $ActionAdd = $TextareaInsertCommands[$FileExt];
        $ActionAdd = str_replace("%filename%","$value",$ActionAdd);
        $Markup .= str_replace("%TextareaInsertCommand%","$ActionAdd",$TextareaInsertTemplate);
        unset($ActionAdd);
      } 
    $Markup .= str_replace("%TextareaInsertCommand%","Attach:$value",$TextareaInsertTemplate);    
    $Size = round(filesize("$uploadsFolder/$value")/1024);
    $Date = date ("Y-m-d H:i:s",filemtime("$uploadsFolder/$value"));
    $Date = str_replace(" ", "<br>",$Date);
    $ActionDelete = "<a href='?action=phAttachman&deletefile=$value' onclick='deleteRow(this); return false;'>Delete</a>";
    
    $result .= "<tr>
      <td class='phAttachman_file'>$File</td>
      <td class='phAttachman_markup'>$Markup</td>
      <td class='phAttachman_size'>$Size</td>
      <td class='phAttachman_date'><small>$Date</small></td>
      ".($EnableDelete ? "<td class='phAttachman_action'>$ActionDelete</td>" : "")."
      </tr>";
    unset($FileExt,$FileType,$File,$Markup,$Size,$Date,$ActionDelete);
  }
  
  $result .= "</table>";
  
  if ($markupCall) return $result;
    else {
      $result .= $HTMLFooterFmt['styles']['phAttachman'];
      $result .= $HTMLFooterFmt['scripts']['phAttachman'];
      exit($result);
    }
}