view admajax.php @ 1096:bbc0a3d0b51e

Major renaming / refactor of site messages. Some that were previously modifiable from admin interface are now "hardcoded" in the configuration file. Having these settings made modifiable from there made no sense and just took space in the UI.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 27 Jan 2017 22:15:06 +0200
parents 95b74632cfe2
children 0a2117349f46 0eca3f1b1d48
line wrap: on
line source

<?php
//
// FAPWeb - Simple Web-based Demoparty Management System
// Party administration page AJAX backend module
// (C) Copyright 2012-2017 Tecnic Software productions (TNSP)
//
$sessionType = "admin";
require_once "mconfig.inc.php";
require_once "msite.inc.php";
require_once "msession.inc.php";


// Define modes for entry editing code
define("EEMODE_NORMAL", 0);
define("EEMODE_EDIT", 1);
define("EEMODE_ADD", 2);


function stValidateRequestCompoData($full, $ctype)
{
  global $previewTypeList;

  $res = TRUE;

  stChkRequestItemFail("name", $fake, $res,
    array(CHK_ISGT, VT_STR, 0, "Compo name is empty."),
    array(CHK_LTEQ, VT_STR, SET_LEN_COMPO_NAME, "Compo name too long (%1 chars, must be less than %2)."));

  stChkRequestItemFail("description", $fake, $res,
    array(CHK_ISGT, VT_STR, 10, "Compo description too short (%1 chars, must be more than %2)"),
    array(CHK_LTEQ, VT_STR, SET_LEN_COMPO_DESC, "Compo description too long (%1 chars, must be less than %2)."));

  // Not a full check?
  if (!$full)
    return $res;

  // Check by compo type
  switch ($ctype)
  {
    case COMPO_NORMAL:
      stChkRequestItemFail("voting", $fake, $res,
        array(CHK_TYPE, VT_BOOL, "Invalid data."));

      stChkRequestItemFail("show_authors", $fake, $res,
        array(CHK_TYPE, VT_BOOL, "Invalid data."));

      stChkRequestItemFail("preview_type", $fake, $res,
        array(CHK_TYPE, VT_INT, "Invalid data."),
        array(CHK_ARRAY_KEY, $previewTypeList, "Invalid preview type value."));

      stChkRequestItemFail("cpath", $fake, $res,
        array(CHK_LTEQ, VT_STR, SET_LEN_COMPO_PATH, "Compo file path too long (%1 chars, must be less than %2)."));
      break;
  }

  stChkRequestItemFail("visible", $fake, $res,
    array(CHK_TYPE, VT_BOOL, "Invalid data."));

  return $res;
}


function stValidateRequestEntryData(&$compo_id, $full, $ctype)
{
  $res = TRUE;

  // Things common for all compo types
  stChkRequestItemFail("name", $fake, $res,
    array(CHK_ISGT, VT_STR, 0, "Name is empty."),
    array(CHK_LTEQ, VT_STR, SET_LEN_ENTRY_NAME, "Name too long (%1 chars, must be less than %2)."));

  stChkRequestItemFail("notes", $fake, $res,
    array(CHK_TYPE, VT_TEXT, "Invalid data."),
    array(CHK_LTEQ, VT_STR, SET_LEN_ENTRY_NOTES, "Entry notes are too long (%1 chars, must be less than %2)."));

  // Check based on compo type
  switch ($ctype)
  {
    case COMPO_NORMAL:
      stChkRequestItemFail("compo_id", $compo_id, $res,
        array(CHK_TYPE, VT_INT, "Invalid compo ID."));

      stChkRequestItemFail("author", $fake, $res,
        array(CHK_ISGT, VT_STR, 0, "Author name not set."),
        array(CHK_LTEQ, VT_STR, SET_LEN_ENTRY_AUTHOR, "Entry author too long (%1 chars, must be less than %2)."));

      stChkRequestItemFail("info", $fake, $res,
        array(CHK_TYPE, VT_TEXT, "Invalid data."),
        array(CHK_LTEQ, VT_STR, SET_LEN_ENTRY_INFO, "Entry info text too long (%1 chars, must be less than %2)."));

      if ($full)
      {
        stChkRequestItemFail("preview_type", $fake, $res,
          array(CHK_TYPE, VT_INT, "Invalid data."),
          array(CHK_RANGE, VT_INT, array(EPREV_NONE, EPREV_AUDIO), "Invalid preview type value."));
      }
      break;
    
    case COMPO_POINTS:
      stChkRequestItemFail("evalue", $fake, $res,
        array(CHK_TYPE, VT_INT, "Invalid points value, must be a valid integer."));
      break;

    case COMPO_ASSIGN:
      stChkRequestItemFail("evalue", $fake, $res,
        array(CHK_TYPE, VT_INT, "Invalid position, must be a valid integer."),
        array(CHK_GTEQ, VT_INT, 1, "Invalid position, must be >= %2."));
      break;
  }

  return $res;
}


function stValidateRequestNewsData()
{
  $res = TRUE;

  stChkRequestItemFail("text", $fake, $res,
    array(CHK_ISGT, VT_STR, 0, "News text too short."),
    array(CHK_LTEQ, VT_STR, SET_LEN_NEWS_TEXT, "News text too long (%1 chars, must be less than %2)."));

  stChkRequestItemFail("author", $fake, $res,
    array(CHK_ISGT, VT_STR, 0, "News author name not set."),
    array(CHK_LTEQ, VT_STR, SET_LEN_NEWS_AUTHOR, "News author name too long (%1 chars, must be less than %2)."));

  stChkRequestItemFail("title", $fake, $res,
    array(CHK_ISGT, VT_STR, 0, "News title not set."),
    array(CHK_LTEQ, VT_STR, SET_LEN_NEWS_TITLE, "News title too long (%1 chars. must be less than %2)."));

  return $res;
}


function stGetCompoData($id, $item, $prefix)
{
  global $compoModeData, $previewTypeList;

  switch ($item["ctype"])
  {
    case COMPO_NORMAL:
      $str1 = 
        "  File path: ".stGetFormTextInput(40, SET_LEN_COMPO_PATH, "cpath", $id, $prefix, $item["cpath"])."<br />\n";

      $str2 =
        "  ".stGetFormCheckBoxInput("voting", $id, $prefix, $item["voting"],
            "Enable voting", "onChange=\"setCompoData(".$id.",'voting')\"").
        "  ".stGetFormCheckBoxInput("show_authors", $id, $prefix, $item["show_authors"], "Show authors")."\n".
        "  ".stGetFormOptionListFromArray($prefix."preview_type".$id, "  ", FALSE, $previewTypeList, $item["preview_type"], 0, 0)."\n";
      break;

    default:
      $str1 = $str2 = "";
      break;
  }

  return
    "  <h2>#".$id." - ".chentities($item["name"])."</h2>\n".
    "  Type: ".stGetFormOptionListFromArray($prefix."type".$id, "  ", FALSE, $compoModeData, $item["ctype"], 0, 0, "updateCompoType(".$id.")").
    " - ".$compoModeData[$item["ctype"]][1]."<br />\n".
    "  Name: ".stGetFormTextInput(40, SET_LEN_COMPO_NAME, "name", $id, $prefix, $item["name"])."<br />\n".
    $str1.
    "<div class=\"compoDesc\"><h3>Description</h3>".stGetFormTextArea(8, 60, "description", $id, $prefix, $item["description"])."</div>\n".
    "<div class=\"compoDesc\"><h3>Notes (shown in results)</h3>".stGetFormTextArea(8, 60, "notes", $id, $prefix, $item["notes"])."</div>\n".
    "<div>\n".
    "  ".stGetFormCheckBoxInput("visible", $id, $prefix, $item["visible"], "Visible")."\n".
    $str2."\n".
    "  ".stGetFormButtonInput("update", $id, $prefix, "Update", "updateCompo(".$id.")")."\n".
    "  ".stGetFormButtonInput("delete", $id, $prefix, "Delete", "deleteCompo(".$id.")")."\n".
    "</div>\n";
}


function stGetNewsItemData($id, $item, $prefix)
{
  return
    "  <h2>".chentities($item["title"])."</h2>\n".
    "  ".stGetFormTextInput(40, SET_LEN_NEWS_TITLE, "title", $id, $prefix, $item["title"]).
    " - posted ".date("d M Y / H:i", $item["utime"])."<br />\n".
    "  ".stGetFormTextArea(5, 60, "text", $id, $prefix, $item["text"])."<br />\n".
    "  ".stGetFormTextInput(20, SET_LEN_NEWS_AUTHOR, "author",  $id, $prefix, $item["author"])."\n".
    "  ".stGetFormButtonInput("", "upd".$id, $prefix, "Update", "updateNews(".$id.")")."\n".
    "  ".stGetFormButtonInput("", "del".$id, $prefix, "Delete", "deleteNews(".$id.")")."\n";
}


function stGetFileUploadAndSelector($mode, $entry, $type, $file_id, $title)
{
  if ($mode == 2)
    return "";

  $eid = $entry["id"];
  $str = "<div class=\"editControl\"><span class=\"editControlTitle\">".chentities($title)."</span>\n";

  // Show currently selected / active file
  if (($efile = stFetchSQL("SELECT * FROM files WHERE deleted=0 AND id=".$entry[$file_id])) !== false)
  {
    $str .=
      "<div>File: <b>".chentities($efile["filename"])."</b></div>\n".
      "<div>Orig: <b>".chentities($efile["origname"])."</b></div>\n";
  }
  else
  {
    $str .= "<div>No file stored OR selected</div>\n";
  }

  if ($mode == 1)
  {
    // Show active file selection
    // XXX TODO .. $sql = stPrepareSQL("SELECT * FROM files WHERE uploadtype=%s AND entry_id=%d", $type, $eid);

    // Show upload form
    $handler = "admajax.php";
    $str .=
    "     ".stGetFormStart($type."UploadForm".$eid, $handler, FALSE, "enctype=\"multipart/form-data\" id=\"".$type."UploadForm".$eid."\"").
    "      ".stGetFormHiddenInput("action", "upload")."\n".
    "      ".stGetFormHiddenInput("type", $type)."\n".
    "      ".stGetFormHiddenInput("entry_id", $eid)."\n".
    "      <input type=\"file\" name=\"".$type."ToUpload".$eid."\" id=\"".$type."ToUpload".$eid."\">\n".
    "      ".stGetFormButtonInput($type."UploadButton", $eid, "", "Upload",
    "jsStartFileUpload('".$type."UploadForm".$eid."','".$handler."','".$type."ToUpload".$eid.
    "',".stGetSetting($type."MaxSize").", function() { updateEntry(".$entry["compo_id"].",".$eid.", 1) })")."\n".
    "     </form>\n";
  }

  return $str."</div>\n";
}


function stGetCompoVoting($compo, $outer)
{
  return
    ($outer ? "<span id=\"covoting".$compo["id"]."\">" : "").
    stGetFormCheckBoxInput(
    "votingbutton", $compo["id"], "co", $compo["voting"], "Voting ".($compo["voting"] ? "IS ACTIVE" : "disabled"),
    "class=\"votingactive\" onChange=\"updateCompoVoting(".$compo["id"].")\"", "").
    ($outer ? "</span>" : "");
}


function stPrintEntryItemData($item, $row, $tr, $prefix, $compo, $mode)
{
  global $entryFlagsList, $previewTypeList, $compoModeData;

  // Fetch compo data if we don't have it already
  $eid = $item["id"];
  if ($compo === FALSE)
    $compo = stFetchSQL("SELECT * FROM compos WHERE id=".$item["compo_id"]);

  // Output wrapper div only if requested
  if ($tr)
  {
    echo
      "  <div class=\"entryRow ".($row % 2 == 1 ? "rodd" : "reven").
      "\"".($mode == EEMODE_NORMAL ? " id=\"entry".$eid."\" onClick=\"activateEntry(".$eid.", false)\"" : "").">\n";
  }

  // Only show show_id if this is a normal compo and we are not adding
  if ($mode != EEMODE_ADD && $compo["ctype"] == COMPO_NORMAL)
  {
    echo
      "   <div class=\"entryCell entryShowID\">".($item["show_id"] > 0 ? $item["show_id"] : "-")."</div>\n";
  }
  
  switch ($compo["ctype"])
  {
    case COMPO_NORMAL:
      echo
      "   <div class=\"entryCell entryBasic\">\n".
      "   ".stGetEditFormTextInput($mode, "Name", 20, SET_LEN_ENTRY_NAME, "name", $eid, $prefix, $item["name"])."\n".
      "   ".stGetEditFormTextInput($mode, "Author", 20, SET_LEN_ENTRY_AUTHOR, "author", $eid, $prefix, $item["author"])."\n".
      "   </div>\n".
      "   <div class=\"entryCell entryFiles\">\n".
      ($mode == 2 ? "Files can be uploaded after adding the entry" : "").
      stGetFileUploadAndSelector($mode, $item, "entry", "file_id", "Entry file").
      stGetFileUploadAndSelector($mode, $item, "preview", "preview_id", "Preview file").
      "   </div>\n".
      "   <div class=\"entryCell entryNotes\">\n".
      "    ".stGetEditFormTextArea($mode, "Info shown during compo", 2, 35, "info", $eid, $prefix, $item["info"])."\n".
      "    ".stGetEditFormTextArea($mode, "Notes for internal use", 2, 35, "notes", $eid, $prefix, $item["notes"])."\n".
      "   </div>\n".
      "   <div class=\"entryCell\">\n";

      if ($mode == EEMODE_NORMAL || $mode == EEMODE_EDIT)
      {
        echo "   <div class=\"editControl entryPreview\">".
          "<span class=\"editControlTitle\">Preview</span>";
          $previewTypeList[$compo["preview_type"]][0]." / ";

        if ($mode)
          echo stGetFormOptionListFromArray($prefix."preview_type".$eid, "    ", FALSE, $previewTypeList, $item["preview_type"], 0, 1);
        else
          echo $previewTypeList[$item["preview_type"]][1];

        stPrintPreviewElements($compo, $item);

        echo "</div>\n";
      }

      if ($mode == EEMODE_EDIT)
      {
        $sql =
          "SELECT compos.*, ".
          "COUNT(DISTINCT entries.id) AS nentries ".
          "FROM compos LEFT JOIN entries ON compos.id=entries.compo_id ".
          "GROUP BY compos.id ".
          "ORDER BY compos.id DESC";

        echo
          "   <div class=\"editControl entryCompoID\">".
          "<span class=\"editControlTitle\">Compo</span>".
          stGetFormOptionListStart($prefix."compo_id".$eid, "    ", TRUE, 0);

        foreach (stExecSQL($sql) as $cdata)
        {
          echo stGetFormOptionListItem("     ",
            $cdata["id"],
            ($cdata["id"] == $item["compo_id"]),
            sprintf("%-20s (%d)", substr($cdata["name"], 0, 20), $cdata["nentries"])
            );
        }

        echo
          stGetFormOptionListEnd("    ", TRUE).
          "</div>\n";
      }
      
      echo "</div>\n";
      break;
    
    case COMPO_POINTS:
    case COMPO_ASSIGN:
      echo
      "   <div class=\"entryCell entryBase\">".
      stGetEditFormTextInput($mode, "Name", 15, SET_LEN_ENTRY_AUTHOR, "name", $eid, $prefix, $item["name"]).
      "</div>\n".
      "   <div class=\"entryCell entryEvalue\">".
      stGetEditFormTextInput($mode, $compoModeData[$compo["ctype"]][2], 5, SET_LEN_ENTRY_AUTHOR, "evalue", $eid, $prefix, $item["evalue"]).
      "</div>\n".
      "   <div class=\"entryCell entryNotes\">\n".
      "    ".stGetEditFormTextArea($mode, "Notes", 2, 30, "notes", $eid, $prefix, $item["notes"])."\n".
      "   </div>\n";
      break;
  }

/* XXX TODO .. flags disabled for now
  if ($mode == EEMODE_NORMAL || $mode == EEMODE_EDIT)
  {
    echo "   <div class=\"entryCell\">";
    foreach ($entryFlagsList as $flag => $fdata)
    {
      echo "     ".stGetFormCheckBoxInput("eflag".$flag, $eid, $prefix, ($item["flags"] & $flag), $fdata[0],
        $mode ? "" : " disabled=\"disabled\" ")."\n";
    }
    echo "   </div>\n";
  }
*/

  if ($mode != EEMODE_NORMAL)
  {
    echo "<div class=\"entryCell entryActions\">\n";
    switch ($mode)
    {
      case EEMODE_EDIT:
        echo
          stGetFormButtonInput("update", $eid, $prefix, "Update", "updateEntry(".$item["compo_id"].",".$eid.", 0)").
          stGetFormButtonInput("delete", $eid, $prefix, "Delete", "deleteEntry(".$item["compo_id"].",".$eid.")");
        break;
      
      case EEMODE_ADD:
        echo
          stGetFormButtonInput("add", $item["compo_id"], $prefix, "Add new", "addEntry(".$item["compo_id"].")");
        break;
    }
    echo "</div>\n";
  }

  if ($tr)
    echo "  </div>\n";
}


function stGetUserKeyClass($item)
{
  global $setUserKeyMode;
  
  switch ($setUserKeyMode)
  {
    case VOTE_FREELY:
      $cond = 0;
      break;

    case VOTE_ACTIVATE:
      $cond = $item["active"];
      break;

    case VOTE_ASSIGN:
      $cond = $item["key_id"] > 0;
      break;
  }

  return
    "userkey ".($cond ? "vkeyActive" : "vkeyInactive").
    " ".($item["nvotes"] > 0 ? "vkeyUsed" : "vkeyUnused");
}


function stGetUserKeyInfo()
{
  $nkeys = stFetchSQLColumn("SELECT COUNT(*) FROM userkeys WHERE active=1");
  $totalKeys = stFetchSQLColumn("SELECT COUNT(*) FROM userkeys");
  
  $nvoters = stFetchSQLColumn("SELECT COUNT(DISTINCT(key_id)) FROM votes");

  return
    "Info: <b>".$nkeys."</b> of <b>".$totalKeys."</b> userkeys are activated. ".
    "Also, <b>".$nvoters."</b> keys have been used for voting.";
}


function stGetUserKeyItemData($id, $item, $prefix)
{
  global $setUserKeyMode, $setUserKeyLen;

  switch ($setUserKeyMode)
  {
    case VOTE_FREELY:
    case VOTE_ACTIVATE:
      $klen = $setUserKeyLen - strlen($item["key"]);
      $str = sprintf(
        "<span class=\"keyid\">%03d</span>&nbsp;:&nbsp;".
        "<span class=\"keycode\">%s</span>",
        $id,
        (($klen > 0) ? str_repeat("&nbsp;", $klen) : "").$item["key"]);

      if ($setUserKeyMode == VOTE_ACTIVATE)
      {
        $str .= stGetFormCheckBoxInput("active", $id, $prefix, $item["active"], FALSE,
          "class=\"keyactive\" onChange=\"userKeySetActive(".$id.")\"", "");
      }
      
      return $str;

    case VOTE_ASSIGN:
      $str =
        "  <td class=\"name\">".chentities($item["name"])."</td>\n".
        "  <td class=\"groups\">".chentities($item["groups"])."</td>\n".
        "  <td class=\"vkeynum\">".
          stGetFormTextInput(5, 5, "key_id", $id, $prefix, $item["key_id"]).
          stGetFormButtonInput("assign", $id, $prefix, "Set", "userKeyAssign(".$id.",1)");

        if ($item["key_id"] != 0)
          $str .= stGetFormButtonInput("clear", $id, $prefix, "Clear", "userKeyAssign(".$id.",0)");
        
      $str .=
        "</td>\n".
        "  <td class=\"vkey\">";

      if ($item["key_id"] > 0)
        $str .= sprintf("<span class=\"keyid\">%03d</span>".
          "&nbsp;:&nbsp;<span class=\"keycode\">%s</span>",
          $item["key_id"], chentities($item["key"]));

      $str .= "</td>\n";
      return $str;
  }
}


function stGetInfoEntryData($show_id, $compo_id, $showNotes)
{
  if ($show_id > 0)
  {
    $sql = stPrepareSQL("SELECT * FROM entries WHERE show_id=%d AND compo_id=%d",
      $show_id, $compo_id);

    if (($entry = stFetchSQL($sql)) !== false)
    {
      // Entry show#/title/author information
      $str =
        "<div class=\"entryInfo\">#".$entry["show_id"]." - ".
        "<span class=\"entryName\">".chentities($entry["name"])."</span>".
        "<span class=\"entryBy\"> by </span>".
        "<span class=\"entryAuthor\">".chentities($entry["author"])."</span>".
        "</div>";

      // File information for quick reference
      if (($efile = stFetchSQL("SELECT * FROM files WHERE deleted=0 AND id=".$entry["file_id"])) !== false)
      {
        foreach (array("S" => "filename", "O" => "origname") as $ftitle => $fid)
        {
          $str .=
            "<div class=\"entryFile\">".
            "<span class=\"entryFileTitle\">".$ftitle.": </span>".
            "<span class=\"entryFileName\">".chentities($efile[$fid])."</span>".
            "</div>";
        }
      }
      else
        $str .= "<div class=\"entryFile\"><span class=\"entryFileName\">No file!</span></div>";

      // Show entry notes here too
      if ($showNotes)
        $str .= "<div class=\"entryNotes\">".chentities($entry["notes"])."</div>";

      return $str;
    }
  }
  return "-";
}


function stGetInfoCurrCompoData($indent)
{
  $compoID = stGetDisplayVar("compoID");
  if ($compoID > 0)
  {
    $sql = stPrepareSQL("SELECT * FROM compos WHERE id=%d", $compoID);
    if (($entry = stFetchSQL($sql)) !== false)
      $strCompo = chentities($entry["name"]);

    $strCurrEntry = stGetInfoEntryData(stGetDisplayVar("compoCurrEntry"), $compoID, TRUE);
    $strPrevEntry = stGetInfoEntryData(stGetDisplayVar("compoPrevEntry"), $compoID, FALSE);
  }
  else
    $strCompo = $strCurrEntry = $strCurrEntryFile = $strPrevEntry = "-";

  return
    $indent."<div class=\"entryData\">Compo: <b>".$strCompo."</b></div>\n".
    $indent."<div class=\"entryData\"><div class=\"entryDataTitle\">Current entry:</div>".$strCurrEntry."</div>\n".
    $indent."<div class=\"entryData\"><div class=\"entryDataTitle\">Previous entry:</div>".$strPrevEntry."</div>\n";
}


function stGetInfoCurrEntryList($indent, $outer)
{
  $sql = stPrepareSQL(
    "SELECT * FROM entries WHERE compo_id=%d ORDER BY show_id ASC",
    stGetDisplayVar("compoID"));

  $currShowID = stGetDisplayVar("compoCurrEntry");
  if ($currShowID <= 0)
    $currShowID = -1;

  $str = stGetFormOptionListStart("ctrlEntryList", $indent, $outer);

  if (($res = stExecSQL($sql)) !== false)
  {
    foreach ($res as $item)
    {
      $str .= stGetFormOptionListItem($indent."  ", $item["show_id"],
        ($item["show_id"] == $currShowID),
        sprintf("%3d. %-25s by %-15s",
          $item["show_id"],
          substr($item["name"], 0, 25),
          substr($item["author"], 0, 15)));
    }
  }
  
  return $str.stGetFormOptionListEnd($indent, $outer);
}


function stGetInfoRotationLists($indent, $outer)
{
  $sql =
    "SELECT rot_list_data.*, ".
    "(SELECT COUNT(*) FROM rot_list_slides WHERE list_id=rot_list_data.id) AS nslides ".
    "FROM rot_list_data ".
    "ORDER BY id DESC";

  $str = stGetFormOptionListStart("ctrlRotationLists", $indent, $outer);
  $currListID = stGetDisplayVar("rotateList");

  if (($res = stExecSQL($sql)) !== false)
  {
    foreach ($res as $item)
    {
      $str .= stGetFormOptionListItem($indent."  ", $item["id"],
        ($currListID == $item["id"]),
        $item["name"]." (".$item["nslides"]." slides)");
    }
  }

  return $str.stGetFormOptionListEnd($indent, $outer);
}


function stGetInfoDisplaySlides($indent, $outer)
{
  $str = stGetFormOptionListStart("ctrlDisplaySlides", $indent, $outer);

  $sql = "SELECT * FROM display_slides ORDER BY id DESC";
  if (($res = stExecSQL($sql)) !== false)
  {
    foreach ($res as $item)
      $str .= stGetFormOptionListItem($indent."  ", $item["id"], FALSE, $item["title"]);
  }

  return $str.stGetFormOptionListEnd($indent, $outer);
}


function stGetInfoRotationListEditFull($indent, $outer, $list_id)
{
  $sql = stPrepareSQL("SELECT * FROM rot_list_data WHERE id=%d", $list_id);
  if (($data = stFetchSQL($sql)) === false)
    return "<p>No such rotation list ID #".intval($list_id)."</p>";
  
  $str =
    ($outer ? $indent."<div class=\"ctrlBox\" id=\"ctrlRotationListEdit\">\n" : "").
    $indent."  <div class=\"ctrlTitle\">Edit rotation list</div>\n".
    $indent."  ".stGetFormTextInput(30, SET_LEN_ROT_LIST_NAME, "", "ctrlEDRotationListName", "", $data["name"])."\n".
    $indent."  ".stGetFormButtonInput("updname", "", "", "Save", "updateRotationList(".$list_id.")")."\n".
    "<div>Available slides:</div>\n".
    stGetFormOptionListStart("ctrlEDDisplaySlides", $indent."  ", TRUE);

  $sql = "SELECT * FROM display_slides";
  if (($res = stExecSQL($sql)) !== false)
  {
    foreach ($res as $item)
      $str .= stGetFormOptionListItem($indent."    ", $item["id"], FALSE, $item["title"]);
  }

  $str .=
    stGetFormOptionListEnd($indent."  ", TRUE).
    "<div>List content:</div>\n".
    stGetInfoRotationListEditData($indent."  ", TRUE, $list_id).
    $indent."  <div class=\"ctrlButtons\">\n".
//    $indent."    ".stGetFormButtonInput("moveslideup", "", "", "Move Up", "moveRotationListSlide(".$list_id.", -1)")."\n".
//    $indent."    ".stGetFormButtonInput("moveslidedn", "", "", "Move Down", "moveRotationListSlide(".$list_id.", 1)")."\n".
    $indent."    ".stGetFormButtonInput("addslide", "", "", "Add slide", "addRotationListSlide(".$list_id.")")."\n".
    $indent."    ".stGetFormButtonInput("delslide", "", "", "Remove slide", "removeRotationListSlide(".$list_id.")")."\n".
    $indent."    ".stGetFormButtonInput("closeedit", "", "", "Close", "jsCloseAdminPopup()")."\n".
    $indent."  </div>\n".
    ($outer ? $indent."</div>\n" : "");

  return $str;
}


function stGetInfoRotationListEditData($indent, $outer, $list_id)
{
  $sql = stPrepareSQL(
    "SELECT display_slides.*,rot_list_slides.order_num FROM display_slides ".
    "LEFT JOIN rot_list_slides ON display_slides.id=rot_list_slides.slide_id ".
    "WHERE rot_list_slides.list_id=%d ".
    "ORDER BY rot_list_slides.order_num DESC",
    $list_id);

  $str = stGetFormOptionListStart("ctrlEDRotationList", $indent, $outer);

  if (($res = stExecSQL($sql)) !== false)
  {
    foreach ($res as $item)
    {
      $str .= stGetFormOptionListItem($indent."  ", $item["id"]."_".$item["order_num"], FALSE, $item["title"]);
    }
  }

  return $str.stGetFormOptionListEnd($indent, $outer);
}


function stGetInfoActiveRotationList($indent, $outer)
{
  $sql = stPrepareSQL("SELECT * FROM rot_list_data WHERE id=%d",
    stGetDisplayVar("rotateList"));
  
  $str = $indent.($outer ? "<div id=\"ctrlActiveRotationList\">" : "").
    "<b>Active list:</b> ";

  if (($slist = stFetchSQL($sql)) === false)
    $str .= "-";
  else
    $str .= chentities(substr($slist["name"], 0, 40));

  return $str.($outer ? "</div>\n" : "");
}


function stGetInfoDisplaySlideEdit($indent, $outer, $slide_id)
{
  $prefix = "ctrlDisplaySlide";

  $str =
    ($outer ? "<div class=\"ctrlBox\" id=\"".$prefix."Edit\">\n" : "").
    $indent."  <form method=\"post\" action=\"\" onsubmit=\"return updateDisplaySlide(".$slide_id.")\">\n".
    $indent."    <div class=\"ctrlTitle\">Edit display slide</div>\n";

  $sql = stPrepareSQL("SELECT * FROM display_slides WHERE id=%d", $slide_id);
  if (($slide = stFetchSQL($sql)) !== false)
  {
    $str .=
      "    ".stGetFormTextInput(40, SET_LEN_DISP_SLIDE_TITLE, "", "Title", $prefix, $slide["title"])."<br />\n".
      "    ".stGetFormTextArea(10, 80, "", "Text", $prefix, $slide["text"])."<br />\n";
  }

  $str .=
    "    <div class=\"ctrlButtons\">\n".
    "      ".stGetFormSubmitInput("createslide", "Save slide")."\n".
    "      ".stGetFormButtonInput("cancelslide", "", "", "Close / Cancel", "jsCloseAdminPopup()")."\n".
    "    </div>\n".
    "  </form>\n".
    ($outer ? "</div>\n" : "");
  
  return $str;
}


function stGetSaveButton()
{
  return "<input type=\"submit\" value=\" Save \" />\n";
}


function stGetShowModeButton($mode, $name, $cmode)
{
  return
    "<input type=\"radio\" id=\"showMode".$mode.
    "\" name=\"showMode\" value=\"".$mode."\" ".
    "onChange=\"setShowMode(".$mode.")\" ".
    ($cmode == $mode ? "checked=\"checked\" ": "")."/>".
    "<label for=\"showMode".$mode."\">".chentities($name)."</label>";
}


function stRandomizeCompoShowOrder($compo_id, $patch)
{
  $entries = stExecSQL("SELECT id,show_id FROM entries WHERE compo_id=".$compo_id);
  if ($entries !== FALSE)
  {
    $ncount = stFetchSQLColumn("SELECT COUNT(*) FROM entries WHERE compo_id=".$compo_id." AND show_id<>0");
    $final = array();

    if ($patch && $ncount > 0)
    {
      $index = -1;
      foreach ($entries as $entry)
      {
        if ($entry["show_id"] == 0)
          $final[] = $entry["id"];

        if ($entry["show_id"] > $index)
          $index = $entry["show_id"];
      }

      $index++;
    }
    else
    {
      foreach ($entries as $entry)
        $final[] = $entry["id"];

      shuffle($final);
      $index = 1;
    }

    stDBBeginTransaction();
    foreach ($final as $entry)
    {
      $sql = stPrepareSQL("UPDATE entries SET show_id=%d WHERE id=%d", $index, $entry);
      if (stExecSQL($sql) === false)
      {
        stError("Error updating entry show positions.");
        break;
      }
      $index++;
    }
    stDBCommitTransaction();
  }
}


//
// Check if we are allowed to execute
//
if (!stCheckHTTPS() || !stAdmSessionAuth(TRUE) || !stCSRFCheck())
{
  stSetStatus(903, "Session expired.");
  stSetupCacheControl();
  stDumpAJAXStatusErrors();

  stSessionEnd(SESS_ADMIN);

  echo
    "<h1>Session expired</h1>".
    "<div><a href=\"admin.php\">Click here to relogin</a>.</div>\n";

  exit;
}


//
// Initialize
//
ob_start();

stSetupCacheControl();

if (!stConnectSQLDB())
  die("Could not connect to SQL database.");

stReloadSettings();
stReloadDisplayVars();
stSessionExpire(SESS_ADMIN, FALSE);


$setUserKeyMode = stGetSetting("userKeyMode");
$setUserKeyLen = stGetSetting("userKeyLength");
$type = stGetRequestItem("type", "");
switch (stGetRequestItem("action", ""))
{
  case "upload":
    //
    // File upload
    //
    if (stHandleGenericFileUpload(0))
    {
      echo "File upload successful!";
      stSetStatus(902, "File successfully uploaded.");
    }
    break;

  case "randomize":
    //
    // Randomize entries display order
    //
    $patch = intval(stGetRequestItem("patch", 1));
    if ($type == "all")
    {
      if (($compos = stExecSQL("SELECT id FROM compos")) === FALSE)
        stError("Eh? SQL error occured.");
      else
      foreach ($compos as $compo)
        stRandomizeCompoShowOrder($compo["id"], $patch);
    }
    else
    if ($type == "compo")
    {
      if (stChkRequestItem("id", $compo_id, array(CHK_TYPE, VT_INT, "Invalid data.")))
        stRandomizeCompoShowOrder($compo_id, $patch);
    }
    break;

  case "screencmd":
    if (stChkRequestItem("cmd", $stmp, array(CHK_TYPE, VT_STR, "Invalid data.")))
    {
      stSetDisplayVar("screenCmdSet", TRUE);
      stSetDisplayVarUpd("screenCmd", $stmp);
    }
    break;

  case "check":
    //
    // Perform systems check
    //
    $errors = 0;
    echo
      "<h1>Competitions / voting</h1>\n".
      "<ul>\n";
    
    echo "<li>".(stGetSetting("allowVoting") ? "Voting <b>IS ENABLED</b>." : "Voting is NOT enabled!")."</li>\n";

    if (($compos = stExecSQL("SELECT * FROM compos")) === FALSE)
      stError("Eh? SQL error occured.");
    else
    {
      foreach ($compos as $compo)
      {
        $nentries = stFetchSQLColumn("SELECT COUNT(*) FROM entries WHERE show_id=0 AND compo_id=".$compo["id"]);
        if ($nentries > 0)
        {
          echo "<li>Compo <b>#".$compo["id"]." - ".$compo["name"]."</b> has NO show order set for some entries.</li>\n";
          $errors++;
        }
      }
    }
    
    $nenabled = stFetchSQLColumn("SELECT COUNT(*) FROM compos WHERE visible<>0 AND voting<>0");
    if ($nenabled == 0)
      echo "<li>No competitions that are visible and enabled for voting.</li>\n";

    $nenabled = stFetchSQLColumn("SELECT COUNT(*) FROM compos WHERE visible=0 AND voting<>0");
    if ($nenabled > 0)
      echo "<li>".$nenabled." competitions that are NOT visible, but are enabled for voting?</li>\n";

    // Count entries and compos
    $nentries = $ncompos = 0;
    foreach (stExecSQL("SELECT * FROM compos WHERE ctype=".COMPO_NORMAL) as $compo)
    {
      if (($ne = stFetchSQLColumn("SELECT COUNT(*) FROM entries WHERE compo_id=".$compo["id"])) !== false && $ne > 0)
      {
        $nentries += $ne;
        $ncompos++;
      }
    }
    if ($ncompos > 0)
      echo "<li>VOTING COMPOS: <b>".$nentries."</b> entries in <b>".$ncompos."</b> compos.</li>\n";

    $nentries = $ncompos = 0;
    foreach (stExecSQL("SELECT * FROM compos WHERE ctype <> ".COMPO_NORMAL) as $compo)
    {
      if (($ne = stFetchSQLColumn("SELECT COUNT(*) FROM entries WHERE compo_id=".$compo["id"])) !== false && $ne > 0)
      {
        $nentries += $ne;
        $ncompos++;
      }
    }
    if ($ncompos > 0)
      echo "<li>NON-VOTING COMPOS: <b>".$nentries."</b> entries/participants in <b>".$ncompos."</b> compos.</li>\n";

    
    if ($errors == 0)
      echo "<li>No errors/warnings detected.</li>\n";

    echo "</ul>\n";
    
    break;

  case "ctrl":
    //
    // Party information system control
    //
    stDBBeginTransaction();
    switch ($type)
    {
      case "setRotateDuration":
        if (stChkRequestItem("duration", $duration,
          array(CHK_TYPE, VT_INT, "Invalid data."),
          array(CHK_RANGE, VT_INT, array(5, 60), "Invalid slide time value, must be 5 - 60 seconds.")))
        {
          stSetDisplayVarUpd("rotateDuration", $duration);
        }
        break;

      case "setActiveRotationList":
        if (stChkRequestItem("id", $list_id, array(CHK_TYPE, VT_INT, "Invalid data.")))
        {
          stSetDisplayVarUpd("rotateList", $list_id);
        }
        break;

      case "setShowMode":
        if (stChkRequestItem("mode", $mode,
          array(CHK_TYPE, VT_INT, "Invalid data."),
          array(CHK_RANGE, VT_INT, array(SMODE_DISABLED, SMODE_COMPO), "Invalid mode value.")))
        {
          stSetDisplayVarUpd("showMode", $mode);
        }
        break;

      case "setCompoID":
        if (stChkRequestItem("id", $compo_id, array(CHK_TYPE, VT_INT, "Invalid data.")))
        {
          if (stFetchSQL("SELECT id FROM compos WHERE id=".$compo_id) === false)
            stError("Invalid compo ID ".$compo_id);
          else
          {
            stSetDisplayVarUpd("compoID", $compo_id);
            stSetDisplayVar("compoCurrEntry", 0);
            stSetDisplayVar("compoPrevEntry", 0);
          }
        }
        break;

      case "setEntry":
      case "nextEntry":
      case "prevEntry":
        if (($compo_id = stGetDisplayVar("compoID")) > 0)
        {
          $prev = $curr = stGetDisplayVar("compoCurrEntry");
          $nentries = stFetchSQLColumn("SELECT COUNT(*) FROM entries WHERE compo_id=".$compo_id);
          switch ($type)
          {
            case "setEntry":
              if (stChkRequestItem("index", $tmp,
                array(CHK_TYPE, VT_INT, "Invalid index.")))
                $curr = $tmp;
              break;

            case "nextEntry":
              if ($curr < $nentries)
                $curr++;
              break;

            case "prevEntry":
              if ($curr > 1)
                $curr--;
              break;
          }

          if (!$errorSet)
          {
            stSetDisplayVar("compoCurrEntry", $curr);
            if ($curr != $prev)
              stSetDisplayVar("compoPrevEntry", $prev);
            stDisplayUpdated();
          }
        }
        else
          stError("No valid competition set.");
        break;

      case "setTempSlide":
        if (stChkRequestItem("id", $slide_id, array(CHK_TYPE, VT_INT, "Invalid data.")) &&
            stChkRequestItem("duration", $slide_dur, array(CHK_RANGE, VT_INT, array(1, 60), "Invalid duration range, should be 1-60 min.")))
        {
          $sql = stPrepareSQL("SELECT * FROM display_slides WHERE id=%d", $slide_id);
          if (($slide = stFetchSQL($sql)) !== false)
          {
            stSetDisplayVar("tempDuration", $slide_dur);
            stSetDisplayVar("tempSlide", $slide["id"]);
            stSetDisplayVar("tempSlideSet", TRUE);
            echo "Temporary slide '".chentities($slide["title"])."' set for <b>".$slide_dur."</b> minutes.";
            stDisplayUpdated();
          }
          else
            stError("No such slide ID #".$slide_id);
        }
        break;

      case "skipToNextSlide":
        if (stGetDisplayVar("activeSlideMode") == SMODE_ROTATE)
        {
          stSetDisplayVar("activeSlideExpire", 0);
          stDisplayUpdated();
        }
        break;

      case "copyDisplaySlide":
        if (stChkRequestItem("id", $slide_id, array(CHK_TYPE, VT_INT, "Invalid data.")))
        {
          $sql = stPrepareSQL("SELECT * FROM display_slides WHERE id=%d", $slide_id);
          if (($slide = stFetchSQL($sql)) !== false)
          {
            $sql = stPrepareSQL("INSERT INTO display_slides (title,text) VALUES (%s,%s)",
              $slide["title"]." (copy)", $slide["text"]);

            if (($new_id = stExecSQLInsert($sql)) !== false)
              echo stGetInfoDisplaySlideEdit("", TRUE, $new_id);
            else
              stError("Could not insert slide.");
          }
          else
            stError("No such slide ID #".$slide_id);
        }
        break;

      case "newDisplaySlide":
        $sql = stPrepareSQL("INSERT INTO display_slides (title) VALUES (%s)", "New slide");
        if (($slide_id = stExecSQLInsert($sql)) !== false)
          echo stGetInfoDisplaySlideEdit("", TRUE, $slide_id);
        break;

      case "updateDisplaySlide":
        if (stChkRequestItem("id", $slide_id, array(CHK_TYPE, VT_INT, "Invalid data.")) &&
            stChkRequestItem("title", $fake,
              array(CHK_GTEQ, VT_STR, 1, "Slide title too short."),
              array(CHK_LTEQ, VT_STR, SET_LEN_DISP_SLIDE_TITLE, "Slide title too long.")
            ) &&
            stChkRequestItem("text", $fake,
              array(CHK_LTEQ, VT_STR, SET_LEN_DISP_SLIDE_TEXT, "Slide content too long.")
            ))
        {
          $sql = stPrepareSQLUpdate("display_slides",
            "WHERE id=".$slide_id,
            array(
              "title" => "S",
              "text" => "S",
            ));

          if (stExecSQL($sql) !== false)
          {
            stSetStatus(200, "Slide updated.");
            stDisplayUpdated();
          }
        }
        break;

      case "deleteDisplaySlide":
        if (stChkRequestItem("id", $slide_id, array(CHK_TYPE, VT_INT, "Invalid data.")))
        {
          $sql = stPrepareSQL("DELETE FROM display_slides WHERE id=%d", $slide_id);
          stExecSQLCond($sql, "Slide deleted.");

          $sql = stPrepareSQL("DELETE FROM rot_list_slides WHERE slide_id=%d", $slide_id);
          stExecSQLCond($sql, "Slide list refs deleted.");
          stDisplayUpdated();
        }
        break;

      case "newRotationList":
        $sql = stPrepareSQL("INSERT INTO rot_list_data (name) VALUES (%s)", "New list #");
        if (($list_id = stExecSQLInsert($sql)) !== false)
        {
          $sql = stPrepareSQL("UPDATE rot_list_data SET name=%s WHERE id=%d",
            "New list #".$list_id, $list_id);

          if (stExecSQLCond($sql, "OK!") !== false)
            echo stGetInfoRotationListEditFull("", TRUE, $list_id);
        }
        break;

      case "updateRotationList":
        if (stChkRequestItem("id", $list_id, array(CHK_TYPE, VT_INT, "Invalid data.")) &&
            stChkRequestItem("name", $fake,
              array(CHK_GTEQ, VT_STR, 3, "Rotation list name too short."),
              array(CHK_LTEQ, VT_STR, SET_LEN_ROT_LIST_NAME, "Rotation list name too long.")))
        {
          $sql = stPrepareSQLUpdate("rot_list_data",
            "WHERE id=".$list_id,
            array(
              "name" => "S",
            ));

          stExecSQLCond($sql, "OK, list updated.");
          stDisplayUpdated();
        }
        stDisplayUpdated();
        break;

      case "deleteRotationList":
        if (stChkRequestItem("id", $list_id, array(CHK_TYPE, VT_INT, "Invalid data.")))
        {
          $sql = stPrepareSQL("DELETE FROM rot_list_data WHERE id=%d", $list_id);
          stExecSQLCond($sql, "List data deleted.");

          $sql = stPrepareSQL("DELETE FROM rot_list_slides WHERE list_id=%d", $list_id);
          stExecSQLCond($sql, "List slide refs deleted.");
          stDisplayUpdated();
        }
        break;

      case "moveRotationListSlide":
        if (stChkRequestItem("list_id", $list_id, array(CHK_TYPE, VT_INT, "Invalid data.")) &&
            stChkRequestItem("slide_id", $slide_id, array(CHK_TYPE, VT_INT, "Invalid data.")) &&
            stChkRequestItem("order_num", $order_num, array(CHK_TYPE, VT_INT, "Invalid data.")) &&
            stChkRequestItem("dir", $dir, array(CHK_TYPE, VT_INT, "Invalid data.")))
        {
        }
        break;

      case "addRotationListSlide":
        if (stChkRequestItem("list_id", $list_id, array(CHK_TYPE, VT_INT, "Invalid data.")) &&
            stChkRequestItem("slide_id", $slide_id, array(CHK_TYPE, VT_INT, "Invalid data.")))
        {
          $nslides = stFetchSQLColumn(stPrepareSQL("SELECT COUNT(*) FROM rot_list_slides WHERE list_id=%d", $list_id));
          $sql = stPrepareSQL(
            "INSERT INTO rot_list_slides (list_id,slide_id,order_num) VALUES (%d,%d,%d)",
            $list_id, $slide_id, $nslides+1);

          if (stExecSQLCond($sql, "Slide added to list."))
          {
            stNormalizeListSlideOrder($list_id);
            stDisplayUpdated();
          }
        }
        break;

      case "removeRotationListSlide":
        if (stChkRequestItem("list_id", $list_id, array(CHK_TYPE, VT_INT, "Invalid data.")) &&
            stChkRequestItem("slide_id", $slide_id, array(CHK_TYPE, VT_INT, "Invalid data.")) &&
            stChkRequestItem("order_num", $order_num, array(CHK_TYPE, VT_INT, "Invalid data.")))
        {
          $sql = stPrepareSQL("DELETE FROM rot_list_slides WHERE list_id=%d AND slide_id=%d AND order_num=%d",
            $list_id, $slide_id, $order_num);
          
          if (stExecSQLCond($sql, "List slide refs deleted."))
          {
            stNormalizeListSlideOrder($list_id);
            stDisplayUpdated();
          }
        }
        break;
    }
    stDBCommitTransaction();
    break;

  case "get":
    //
    // Get specific data
    //
    switch ($type)
    {
      case "infoCurrCompoData":
        echo stGetInfoCurrCompoData("", FALSE);
        break;

      case "infoCurrEntryList":
        echo stGetInfoCurrEntryList("", FALSE);
        break;
      
      case "infoRotationLists":
        echo stGetInfoRotationLists("", FALSE);
        break;
    
      case "infoDisplaySlides":
        echo stGetInfoDisplaySlides("", FALSE);
        break;

      case "infoRotationListEdit":
        if (stChkRequestItem("id", $list_id, array(CHK_TYPE, VT_INT, "Invalid data.")))
          echo stGetInfoRotationListEditFull("", stGetRequestItem("full", TRUE), $list_id);
        break;

      case "infoRotationListEditData":
        if (stChkRequestItem("id", $list_id, array(CHK_TYPE, VT_INT, "Invalid data.")))
          echo stGetInfoRotationListEditData("", FALSE, $list_id);
        break;

      case "infoDisplaySlideEdit":
        if (stChkRequestItem("id", $slide_id, array(CHK_TYPE, VT_INT, "Invalid data.")))
          echo stGetInfoDisplaySlideEdit("", TRUE, $slide_id);
        break;

      case "infoActiveRotationList":
        echo stGetInfoActiveRotationList("", FALSE);
        break;

      case "infoMain":
        //
        // Main information control screen
        //
        $showMode = stGetDisplayVar("showMode");
        echo
          "<div id=\"ctrlModeControls\" class=\"ctrlModeControls\">\n".
          "Active mode:\n".
          stGetShowModeButton(SMODE_DISABLED, "Off/disabled", $showMode)."\n".
          stGetShowModeButton(SMODE_ROTATE, "Slide rotation", $showMode)."\n".
          stGetShowModeButton(SMODE_COMPO, "Compo mode", $showMode)."\n".
          stGetFormButtonInput("openShowScreen", "", "", "Showscreen window", "window.open('show.php')")."\n".
          stGetFormButtonInput("reloadShowScreen", "", "", "Showscreen reload", "showScreenCmd('reload')")."\n".
          //"</div>\n".
          //"<div id=\"ctrlSystemControls\">\n".
          stGetFormButtonInput("systemCheck", "", "", "System check", "performSystemCheck()")."\n".
          stGetFormButtonInput("generateShowPositions", "", "", "Add show positions", "generateEntryPositions(0, 1)")."\n".
          // XXX: disable this button for now
          // stGetFormButtonInput("regenerate", "", "", "RESET show positions", "generateEntryPositions(0, 0)")."\n".
          "</div>\n";

        echo
          "<div class=\"ctrlBox\" id=\"ctrlListRotationLists\">\n".
          "  <div class=\"ctrlTitle\">Rotation lists:</div>\n".
          "  <div class=\"ctrlInfo\">Lists of slides, that are shown for X seconds and 'rotated' to next one.</div>\n".
          stGetInfoRotationLists("    ", TRUE).
          "  <div class=\"ctrlButtons\">\n".
          "    ".stGetFormButtonInput("setdur", "", "", "Set", "setRotateDuration()")."\n".
          "    ".stGetFormTextInput(3, 5, "", "ctrlRotSlideDuration", "", stGetDisplayVar("rotateDuration"))." sec\n".
          " - ".
          "    ".stGetFormButtonInput("actlist", "", "", "Set Active", "setActiveRotationList()")."\n".
          "    ".stGetFormButtonInput("editlist", "", "", "Edit", "editRotationList()")."\n".
          "    ".stGetFormButtonInput("newlist", "", "", "New", "newRotationList()")."\n".
          "    ".stGetFormButtonInput("dellist", "", "", "Delete", "deleteRotationList()")."\n".
          "  </div>\n".
          stGetInfoActiveRotationList("  ", TRUE).
          "</div>\n";

        echo
          "<div class=\"ctrlBox\" id=\"ctrlListDisplaySlides\">\n".
          "<div class=\"ctrlTitle\">Display slides:</div>\n".
          "  <div class=\"ctrlInfo\">Editable slides (think 'powerpoint') for information/announcements. ".
          "Can be collected into slide rotations or set to display as 'temp slide' at any given time.</div>\n".
          stGetInfoDisplaySlides("  ", TRUE).
          "  <div class=\"ctrlButtons\">\n".
          "    ".stGetFormButtonInput("editslide", "", "", "Edit", "editDisplaySlide()")."\n".
          "    ".stGetFormButtonInput("copyslide", "", "", "Copy", "copyDisplaySlide()")."\n".
          "    ".stGetFormButtonInput("newslide", "", "", "New", "newDisplaySlide()")."\n".
          "    ".stGetFormButtonInput("delslide", "", "", "Delete", "deleteDisplaySlide()")."\n".
          "  </div>\n".
          "  <div class=\"ctrlButtons\">\n".
          "    ".stGetFormTextInput(3, 5, "", "ctrlTempSlideDuration", "", stGetDisplayVar("tempDuration"))." min\n".
          "    ".stGetFormButtonInput("setslide", "", "", "Set Temp", "activateTempSlide()")."\n".
          "    ".stGetFormButtonInput("nextslide", "", "", "Skip to next", "skipToNextSlide()")."\n".
          "  </div>\n".
          "</div>\n".
          "<div class=\"ctrlBox\" id=\"ctrlCompoControl\">\n".
          "  <div class=\"ctrlTitle\">Competition control:</div>\n".
          "  <div class=\"ctrlInfo\">Controls for competition showing mode. Select and activate desired compo, then ".
          "hit 'Next entry' to activate the first entry to show. <b>Notice! You need to have generated 'show positions' ".
          "before starting compos!</b></div>\n".
          "  <div class=\"ctrlDBox1\">\n".
          stGetFormOptionListStart("ctrlCompoList", "    ", TRUE);

        $sql =
          "SELECT compos.*, ".
          "COUNT(DISTINCT entries.id) AS nentries ".
          "FROM compos LEFT JOIN entries ON compos.id=entries.compo_id ".
          "GROUP BY compos.id ".
          "HAVING COUNT(DISTINCT entries.id) > 0 AND compos.ctype=".COMPO_NORMAL." ".
          "ORDER BY compos.id DESC";

        $currCompoID = stGetDisplayVar("compoID");
        if (($res = stExecSQL($sql)) !== false)
        {
          foreach ($res as $item)
          {
            echo stGetFormOptionListItem("     ", $item["id"],
              ($item["id"] == $currCompoID),
              sprintf("%-20s (%d entries)", substr($item["name"], 0, 20), $item["nentries"]));
          }
        }

        echo
          stGetFormOptionListEnd("    ", TRUE).
          "    <div class=\"ctrlButtons\">\n".
          "      ".stGetFormButtonInput("setcompo", "", "", "Change compo", "activateCompo()")."\n".
          "    </div>\n".
          "  </div>\n".
          "  <div class=\"ctrlDBox1\">\n".
          stGetInfoCurrEntryList("    ", TRUE).
          "    <div class=\"ctrlButtons\">\n".
          "      ".stGetFormButtonInput("setentry", "", "", "Set selected entry", "setSelectedEntry()")."\n".
          "      ".stGetFormButtonInput("preventry", "", "", "Prev entry", "switchEntry(-1)")."\n".
          "      ".stGetFormButtonInput("nextentry", "", "", "Next entry", "switchEntry(1)")."\n".
          "    </div>\n".
          "  </div>\n".
          "  <div class=\"ctrlDBox2\" id=\"ctrlCurrCompoData\">\n".
          stGetInfoCurrCompoData("    ").
          "  </div>\n".
          "</div>\n";
          stGetInfoRotationListEditFull("", TRUE, 0);
        break;

      case "news":
        echo
          "<form method=\"post\" action=\"\" onsubmit=\"return addNews()\">\n".
          "  ".stGetFormTextInput(40, SET_LEN_NEWS_TITLE, "", "nntitle", "", "")."<br />\n".
          "  ".stGetFormTextArea(5, 60, "", "nntext", "", "")."<br />\n".
          "  ".stGetFormTextInput(20, SET_LEN_NEWS_AUTHOR, "", "nnauthor", "", "orgaz")."\n".
          "  ".stGetFormSubmitInput("nnadd", "Add post")."\n".
          "  ".stGetFormButtonInput("", "", "", "Clear", "this.form.reset()")."\n".
          "</form>\n".
          "<hr />\n";

        $sql = "SELECT * FROM news ORDER BY utime DESC";
        foreach (stExecSQL($sql) as $item)
        {
          echo
            "<div id=\"news".$item["id"]."\" class=\"newsItem\">\n".
            stGetNewsItemData($item["id"], $item, "ne").
            "</div>\n";
        }
        break;

      case "newsitem":
        $res = stFetchSQL(stPrepareSQL("SELECT * FROM news WHERE id=%D", "id"));
        if ($res !== FALSE)
          echo stGetNewsItemData($res["id"], $res, "ne");
        break;

      case "attendees":
        echo
          "<div class=\"tabHeadersSub\">\n".
          "<a href=\"print.php?type=emails\" target=\"_blank\">Show plain list of e-mails</a>\n".
          "</div>\n";

        // For adding a new one
        $prefix = "ne";
        echo
          "<table>\n".
          " <tr>\n".
          "  <th>Name</th>\n".
          "  <th>Groups</th>\n".
          "  <th>Oneliner</th>\n".
          "  <th>E-mail</th>\n".
          "  <th>Actions</th>\n".
          " </tr>\n".
          " <tr>\n".
          "  <td>".stGetFormTextInput(20, SET_LEN_USERNAME, "name", "x", $prefix, "")."</td>\n".
          "  <td>".stGetFormTextInput(20, SET_LEN_GROUPS,   "groups", "x", $prefix, "")."</td>\n".
          "  <td>".stGetFormTextInput(30, SET_LEN_ONELINER, "oneliner", "x", $prefix, "")."</td>\n".
          "  <td>".stGetFormTextInput(20, SET_LEN_EMAIL,    "email", "x", $prefix, "")."</td>\n".
          "  <td>".stGetFormButtonInput("add", "", $prefix, " Add new ", "addAttendee()")."</td>\n".
          " </tr>\n".
          "</table>\n".
          "<hr />\n";

        // List of attendees
        echo
          "<table class=\"attendees\">\n".
          " <tr>\n".
          "  <th class=\"name\">Name</th>\n".
          "  <th class=\"groups\">Groups</th>\n".
          "  <th class=\"regtime\">Registered</th>\n".
          "  <th class=\"oneliner\">Oneliner</th>\n".
          "  <th class=\"email\">E-mail</th>\n".
          "  <th class=\"reghost\">RegHost</th>\n".
          "  <th>Actions</th>\n".
          " </tr>\n";

        $sql = "SELECT * FROM attendees ORDER BY regtime DESC";
        $row = 0;
        foreach (stExecSQL($sql) as $item)
          stPrintAttendee($item, $row++, TRUE, TRUE, FALSE);

        echo
          "</table>\n";
        break;

      case "attendee":
        $res = stFetchSQL(stPrepareSQL("SELECT * FROM attendees WHERE id=%D", "id"));
        if ($res !== FALSE)
          stPrintAttendee($res, -1, FALSE, TRUE, stGetRequestItem("edit", FALSE));
        else
          stError("No such attendee ID!");
        break;

      case "userkeyinfo":
        echo stGetUserKeyInfo();
        break;

      case "userkey":
      case "userkeyclass":
        switch ($setUserKeyMode)
        {
          case VOTE_FREELY:
          case VOTE_ACTIVATE:
            $sql = stPrepareSQL("SELECT (SELECT COUNT(id) FROM votes WHERE key_id=%D) AS nvotes,userkeys.* FROM userkeys WHERE id=%D", "id", "id");
            break;

          case VOTE_ASSIGN:
            $sql = stPrepareSQL("SELECT (SELECT COUNT(id) FROM votes WHERE key_id=%D) AS nvotes,userkeys.key,attendees.* FROM attendees ".
              "LEFT JOIN userkeys ON userkeys.id=attendees.key_id ".
              "WHERE attendees.id=%D", "id", "id");
            break;
        }
        if (($res = stFetchSQL($sql)) !== FALSE)
        {
          if ($type == "userkeyclass")
            echo stGetUserKeyClass($res);
          else
            echo stGetUserKeyItemData($res["id"], $res, "vk");
        }
        break;

      case "voters":
        // Generate user keys, if needed
        $numVKeys = stFetchSQLColumn("SELECT COUNT(*) FROM userkeys");
        $numUsers = stFetchSQLColumn("SELECT COUNT(*) FROM attendees");
        if (($tmp = stGetSetting("maxAttendeesHard")) > $numUsers)
          $numUsers = $tmp;
        else
        if (($tmp = stGetSetting("maxAttendeesSoft")) > $numUsers)
          $numUsers = $tmp;

        while ($numVKeys <= $numUsers)
        {
          if (($key = stGenerateUserKey()) !== false)
          {
            if (stExecSQL(stPrepareSQL("INSERT INTO userkeys (key) VALUES (%s)", $key)) !== false)
              $numVKeys++;
          }
        }

        // Some information
        echo
          "<div class=\"tabHeadersSub\">\n".
          " <a href=\"print.php?type=userkeys\" target=\"_blank\">Show printable key list</a>\n".
          " <a href=\"print.php?type=results&flags=".(RFLAG_NORMAL)."\" target=\"_blank\">Printable results</a>\n".
          " <a href=\"print.php?type=results&flags=".(RFLAG_DISQUALIFIED)."\" target=\"_blank\">Printable full results</a> (shows also disqualified entries)\n".
          " <a href=\"print.php?type=results&flags=".(RFLAG_DISQUALIFIED | RFLAG_HIDDEN_COMPOS)."\" target=\"_blank\">Printable FULL results</a> (shows also hidden and empty compos)\n".
          " <div id=\"vkeyInfo\">".stGetUserKeyInfo()."</div>\n".
          "</div>\n";

        echo
          "<div class=\"info\">".
          "User key length ".stGetSetting("userKeyLength")." ".
          "<b>Voting mode: ";
        
        if (isset($voteModeData[$setUserKeyMode]))
          echo $voteModeData[$setUserKeyMode][0]."</b>. ".$voteModeData[$setUserKeyMode][1];
        else
          echo "VOTE MODE NOT SET! CHECK CONFIGURATION!</b>";

        echo "</div>\n";

        // List of userkeys
        switch ($setUserKeyMode)
        {
          case VOTE_FREELY:
          case VOTE_ACTIVATE:
            $sql = "SELECT userkeys.*,COUNT(votes.id) AS nvotes FROM userkeys ".
              "LEFT JOIN votes ON votes.key_id=userkeys.id ".
              "GROUP BY userkeys.id ".
              "ORDER BY userkeys.id ASC";

            foreach (stExecSQL($sql) as $item)
            {
              echo
                "<div class=\"".stGetUserKeyClass($item).
                "\" id=\"vkey".$item["id"]."\">".
                stGetUserKeyItemData($item["id"], $item, "vk").
                "</div>\n";
            }
            break;

          case VOTE_ASSIGN:
            $sql = "SELECT userkeys.*,attendees.*,COUNT(votes.id) AS nvotes FROM attendees ".
              "LEFT JOIN userkeys ON userkeys.id=attendees.key_id ".
              "LEFT JOIN votes ON votes.key_id=attendees.key_id ".
              "GROUP BY userkeys.id ".
              "ORDER BY attendees.regtime DESC";

            echo
              "<table class=\"attendees\">\n".
              " <tr>\n".
              "  <th class=\"name\">Name</th>\n".
              "  <th class=\"groups\">Groups</th>\n".
              "  <th class=\"vkeynum\">Key #</th>\n".
              "  <th class=\"vkey\">Userkey</th>\n".
              " </tr>\n";

            $index = 0;
            foreach (stExecSQL($sql) as $item)
            {
              echo
                " <tr class=\"".stGetUserKeyClass($item).
                "\" id=\"vkey".$item["id"]."\">\n".
                stGetUserKeyItemData($item["id"], $item, "vk").
                " </tr>\n";
            }

            echo
              "</table>\n";
            break;
        }
        break;

      case "compos":
        echo
          "<form method=\"post\" action=\"\" onsubmit=\"return addCompo()\">\n".
          "<b>Name:<b>".
          "  ".stGetFormTextInput(64, SET_LEN_COMPO_NAME, "", "ncname", "", "")."<br />\n".
          "<b>Description:</b>".
          "  ".stGetFormTextArea(5, 60, "", "ncdescription", "", "")."<br />\n".
          "  ".stGetFormSubmitInput("nccompo", "Add compo")."\n".
          "  ".stGetFormButtonInput("", "", "", "Clear", "this.form.reset()")."\n".
          "</form>\n".
          "<hr />\n";

        $sql = "SELECT * FROM compos ORDER BY id DESC";
        foreach (stExecSQL($sql) as $item)
        {
          echo
            "<div id=\"compo".$item["id"]."\" class=\"compoItem\">\n".
            stGetCompoData($item["id"], $item, "co").
            "</div>\n";
        }
        break;

      case "compo":
        $res = stFetchSQL(stPrepareSQL("SELECT * FROM compos WHERE id=%D", "id"));
        if ($res !== FALSE)
          echo stGetCompoData($res["id"], $res, "co");
        else
          stError("No such compo ID!");
        break;

      case "settingslist":
        $index = 0;
        foreach (stExecSQL("SELECT * FROM settings_groups") as $group)
        {
          if ($index++ > 0) echo ",";
          echo "\"".$group["id"]."\":\"".chentities($group["name"])."\"";
        }
        break;

      case "settings":
        $group = stFetchSQL(stPrepareSQL("SELECT * FROM settings_groups WHERE id=%D", "id"));
        if ($group !== FALSE)
        {
          $prefix = "st";
          $first = TRUE;
          echo
            "<h1>".chentities($group["description"])."</h1>\n".
            "<form method=\"post\" action=\"\" onsubmit=\"return jsUpdateSettings(".$group["id"].")\">\n";

          foreach (stExecSQL("SELECT * FROM settings WHERE vtype<>".VT_TEXT." AND vgroup=".$group["id"]." ORDER BY vtype ASC") as $item)
          {
            if ($first)
            {
              echo "<table>\n";
              $first = FALSE;
            }

            echo
              " <tr>\n".
              "  <td>";

            $id = $item["key"];
            switch ($item["vtype"])
            {
              case VT_INT:
                echo stGetFormTextInput(10, 10, "", $id, $prefix, $item["vint"]);
                break;
              case VT_STR:
                echo stGetFormTextInput(40, 128, "", $id, $prefix, $item["vstr"]);
                break;
              case VT_BOOL:
                echo stGetFormCheckBoxInput("", $id, $prefix, $item["vint"], "");
                break;
            }
            echo
              "</td>\n".
              "  <td><label for=\"".$prefix.$id."\">".chentities($item["sdesc"])."</label></td>\n".
              " </tr>\n";
          }
          if (!$first)
            echo "</table>\n".stGetSaveButton();

          foreach (stExecSQL("SELECT * FROM settings WHERE vtype=".VT_TEXT." AND vgroup=".$group["id"]." ORDER BY key DESC") as $item)
          {
            echo
              "<h2>".chentities($item["sdesc"])."</h2>\n".
              stGetFormTextArea(12, 80, "", $item["key"], $prefix, $item["vtext"]).
              "\n<br />\n".
              stGetSaveButton();
          }
          echo "</form>\n";
        }
        break;
      
      case "compolist":
        $index = 0;
        foreach (stExecSQL("SELECT * FROM compos") as $compo)
        {
          $ne = stFetchSQLColumn("SELECT COUNT(*) FROM entries WHERE compo_id=".$compo["id"]);
          if ($index++ > 0) echo ",";
          echo
            "\"".$compo["id"]."\":\"".chentities($compo["name"]).
            (($ne !== false && $ne > 0) ? " <span class='cnotice'>(".$ne.")</span>" : "")."\"";
        }
        break;

      case "compovoting":
        $id = intval(stGetRequestItem("id", 0));
        if (($compo = stFetchSQL("SELECT * FROM compos WHERE id=".$id)) !== false)
          echo stGetCompoVoting($compo, FALSE);
        break;

      case "entries":
        $id = intval(stGetRequestItem("id", 0));
        if (($compo = stFetchSQL("SELECT * FROM compos WHERE id=".$id)) !== false)
        {
          $nentries = stFetchSQLColumn("SELECT COUNT(*) FROM entries WHERE compo_id=".$id);
          $prefix = "ne";
          echo
            "<h1>#".$id." - ".chentities($compo["name"])." (".$nentries." entries)</h1>\n";

          if ($nentries > 0 && $compo["ctype"] == COMPO_NORMAL)
          {
            echo
              stGetFormButtonInput("generate", "", "", " Add missing show positions ", "generateEntryPositions(".$id.", 1)")."\n".
              stGetFormButtonInput("regenerate", "", "", " ReGenerate show positions ", "generateEntryPositions(".$id.", 0)")."\n".
              stGetCompoVoting($compo, TRUE)."\n";
          }

          stPrintEntryItemData(array(
            "id" => $id,
            "show_id" => 0,
            "name" => "",
            "author" => "",
            "compo_id" => $id,
            "info" => "",
            "notes" => "",
            "preview_type" => 0,
            "flags" => 0,
            "evalue" => "",
            "utime" => 0,
            ),
            0, TRUE, "ne", $compo, EEMODE_ADD);

          echo
            "<hr />\n".
            "<div class=\"entries\">\n";

          $row = 0;
          foreach (stExecSQL("SELECT * FROM entries WHERE compo_id=".$id." ORDER BY id DESC") as $entry)
            stPrintEntryItemData($entry, $row++, TRUE, "en", $compo, EEMODE_NORMAL);

          echo "</div>\n";
        }
        break;

      case "entry":
        $res = stFetchSQL(stPrepareSQL("SELECT * FROM entries WHERE id=%D", "id"));
        if ($res !== FALSE)
        {
          stPrintEntryItemData($res, -1, FALSE, "en", FALSE,
            stGetRequestItem("edit", FALSE) ? EEMODE_EDIT : EEMODE_NORMAL);
        }
        else
          stError("No such entry ID!");
        break;
    }
    break;

  case "delete":
    //
    // Delete entry
    //
    stDBBeginTransaction();
    if (stChkRequestItem("id", $id, array(CHK_TYPE, VT_INT, "Invalid data.")))
    {
      if ($type == "news")
      {
        $sql = stPrepareSQL("DELETE FROM news WHERE id=%d AND persist=0", $id);
        stExecSQLCond($sql, "OK, news item ".$id." deleted.");
      }
      else
      if ($type == "attendees")
      {
        // Attendees require some more work
        $sql = stPrepareSQL("SELECT * FROM attendees WHERE id=%d", $id);
        if (($attn = stFetchSQL($sql)) !== false)
        {
          $sql = stPrepareSQL("DELETE FROM attendees WHERE id=%d", $id);
          stExecSQLCond($sql, "OK, attendee ".$id." deleted.");

          // If assigned userkey mode, delete the key and votes as well
          if ($setUserKeyMode == VOTE_ASSIGN && $attn["key_id"] != 0)
          {
            $sql = stPrepareSQL("DELETE FROM userkeys WHERE id=%d", $attn["key_id"]);
            stExecSQLCond($sql, "OK, attendee ".$id." userkey deleted.");

            $sql = stPrepareSQL("DELETE FROM votes WHERE key_id=%d", $attn["key_id"]);
            stExecSQLCond($sql, "OK, attendee ".$id." votes deleted.");
          }
        }
        else
          stError("No such attendee ID #".$id);
      }
      else
      if ($type == "entries")
      {
        stDeleteCompoEntry($id);
      }
      else
      if ($type == "compo")
      {
        // Delete entries for the compo
        foreach (stExecSQL("SELECT * FROM entries WHERE compo_id=".$id) as $sentry)
          stDeleteCompoEntry($sentry["id"]);

        // Delete the compo itself
        stExecSQLCond(
          "DELETE FROM compos WHERE id=".$id,
          "OK, compo ".$id." deleted.");
      }
    }
    else
    if ($type == "userkeys")
    {
      $sql = stPrepareSQL("DELETE FROM userkeys");
      stExecSQLCond($sql, "OK, all userkeys purged");
    }

    stDBCommitTransaction();
    break;

  case "add":
    //
    // Add new entry
    //
    stDBBeginTransaction();
    if ($type == "news" && stValidateRequestNewsData())
    {
      $sql = stPrepareSQL(
        "INSERT INTO news (utime,title,text,author) VALUES (%d,%S,%Q,%S)",
        time(), "title", "text", "author");

      stExecSQLCond($sql, "OK, news item added.");
    }
    else
    if ($type == "compo" && stValidateRequestCompoData(FALSE, 0))
    {
      $sql = stPrepareSQL(
        "INSERT INTO compos (name,description,visible,voting,show_authors,preview_type) VALUES (%S,%Q,0,0,0,0)",
        "name", "description");

      stExecSQLCond($sql, "OK, compo added.");
    }
    else
    if ($type == "attendees" && stValidateRequestUserData(TRUE, FALSE))
    {
      $sql = stGetAttendeeRegistrationSQL();
      stExecSQLCond($sql, "OK, attendee added.");
    }
    else
    if ($type == "entry")
    {
      if (($compo = stFetchSQL(stPrepareSQL("SELECT * FROM compos WHERE id=%D", "compo_id"))) === FALSE)
        stError("No such compo ID.");
      else
      if (stValidateRequestEntryData($cfake, FALSE, $compo["ctype"]))
      {
        // The function gets most of its data from request parameters
        stAddCompoEntry($compo, 0); // User ID = 0 for admin
      }
    }
    stDBCommitTransaction();
    break;

  case "update":
    //
    // Update existing entry
    //
    stDBBeginTransaction();
    if (stChkRequestItem("id", $id, array(CHK_TYPE, VT_INT, "Invalid data.")))
    {
      if ($type == "settings")
      {
        foreach (stExecSQL("SELECT * FROM settings WHERE vgroup=".$id) as $item)
        if (($val = stGetRequestItem($item["key"], FALSE)) !== FALSE)
        {
          $sql = "UPDATE settings SET ".stGetSettingSQL($item, $val)." WHERE key=".$db->quote($item["key"]);
          stExecSQL($sql);
        }
        stSetStatus(200, "Updated settings.");
      }
      else
      if ($type == "attendees" && stValidateRequestUserData(TRUE, $id))
      {
        $sql = stPrepareSQLUpdate("attendees",
          "WHERE id=".$id,
          array(
            "name" => "S",
            "groups" => "S",
            "email" => "S",
            "oneliner" => "S",
            "reghost" => "S",
          ));

        stExecSQLCond($sql, "OK, attendee updated.");
      }
      else
      if ($type == "news" && stValidateRequestNewsData())
      {
        $sql = stPrepareSQLUpdate("news",
          "WHERE id=".$id,
          array(
            "title" => "S",
            "text" => "Q",
            "author" => "S"
          ));

        stExecSQLCond($sql, "OK, news item updated.");
      }
      else
      if ($type == "compotype" &&
        stChkRequestItem("ctype", $compotype,
          array(CHK_RANGE, VT_INT, array(COMPO_NORMAL, COMPO_ASSIGN), "Invalid compo type.")
        ))
      {
        $sql = stPrepareSQL("UPDATE compos SET ctype=%d WHERE id=%d", $compotype, $id);
        stExecSQLCond($sql, "OK, compo updated.");
      }
      else
      if ($type == "compovoting" &&
        stChkRequestItem("voting", $compovoting, $qres,
          array(CHK_TYPE, VT_BOOL, "Invalid data.")
        ))
      {
        $sql = stPrepareSQL("UPDATE compos SET voting=%b WHERE id=%d", $compovoting, $id);
        stExecSQLCond($sql, "OK, compo updated.");
      }
      else
      if ($type == "compo")
      {
        // Check if compo ID exists
        if (($compo = stFetchSQL("SELECT * FROM compos WHERE id=".$id)) === false)
          stError("No such compo ID.");
        else
        if (stValidateRequestCompoData(TRUE, $compo["ctype"]))
        {
          switch ($compo["ctype"])
          {
            case COMPO_NORMAL:
              $cdata = array(
                "voting" => "B",
                "show_authors" => "B",
                "preview_type" => "D",
                "cpath" => "S",
              );
              break;
            
            default:
              $cdata = array();
              break;
          }
          
          $sql = stPrepareSQLUpdate("compos",
            "WHERE id=".$id,
            array_merge(array(
              "name" => "S",
              "description" => "Q",
              "notes" => "Q",
              "visible" => "B",
            ), $cdata));

          stExecSQLCond($sql, "OK, compo updated.");
        }
      }
      else
      if ($type == "entry")
      {
        if (($compo = stFetchSQL(stPrepareSQL("SELECT * FROM compos WHERE id=%D", "compo_id"))) === FALSE)
          stError("No such compo ID.");
        else
        if (stValidateRequestEntryData($compo_id, TRUE, $compo["ctype"]))
        {
          switch ($compo["ctype"])
          {
            case COMPO_NORMAL:
              $cdata = array(
                "author" => "S",
                "info" => "Q",
                "compo_id" => "D",
              );
              break;
            
            case COMPO_POINTS:
            case COMPO_ASSIGN:
              $cdata = array(
                "evalue" => "D",
              );
              break;
          }

          $sql = stPrepareSQLUpdate("entries",
            "WHERE id=".$id,
            array_merge(array(
              "name" => "S",
              "notes" => "Q",
            ), $cdata));

          stExecSQLCond($sql, "OK, entry updated.");
        }
      }
    }
    stDBCommitTransaction();
    break;

  case "userkey":
    //
    // Userkey activation/deactivation handling
    //
    stDBBeginTransaction();
    if (stChkRequestItem("id", $id, array(CHK_TYPE, VT_INT, "Invalid data.")))
    {
      switch ($type)
      {
        case "assign":
          // Check if already assigned to someone ..
          $key_id = intval(stGetRequestItem("key_id", 0));
          $sql = stPrepareSQL("SELECT * FROM userkeys WHERE id=%d", $key_id);
          if (stFetchSQL($sql) === false)
            stError("Invalid key ID #.");
          else
          {
            $sql = stPrepareSQL("SELECT * FROM attendees WHERE key_id=%d", $key_id);
            if (($attn = stFetchSQL($sql)) !== false && $attn["id"] != $id)
              stError("That key has already been assigned to another attendee!");
            else
            {
              // Assign ..
              $sql = stPrepareSQL("UPDATE attendees SET key_id=%d WHERE id=%d", $key_id, $id);
              stExecSQLCond($sql, "Assigned key updated.");
            }
          }
          break;

        case "clear":
          $sql = stPrepareSQL("UPDATE attendees SET key_id=NULL WHERE id=%d", $id);
          stExecSQLCond($sql, "Assigned key cleared.");
          break;
        
        case "active":
          // Autobots activate!
          $sql = stPrepareSQL("UPDATE userkeys SET active=%B WHERE id=%d", "active", $id);
          stExecSQLCond($sql, "Userkey status changed.");
          break;
      }
    }
    stDBCommitTransaction();
    break;

  default:
    stSetStatus(903, "Not Found");
    break;
}

if (!$errorSet)
  stSaveDisplayVars();


if ($errorSet)
{
  ob_clean();
  stDumpAJAXStatusErrors();
}

ob_end_flush();
?>