view index.php @ 139:0ccb459aaecb

Oops. stQM() should've beem cmQM(). Fixed.
author Matti Hamalainen <ccr@tnsp.org>
date Wed, 27 Aug 2014 21:21:29 +0300
parents 01c3784d3a90
children 26d266a685ed
line wrap: on
line source

<?php
//
// OAMK Lukkari v2.3
// (C) Copyright 2010 - 2014 Matti 'ccr' Hämäläinen <ccr@tnsp.org>
// Yes, this code is rather horrible. :|
//
// Include framework
require "mcommon.inc.php";

// Default settings
$pageVersion = "2.3";
$mobileMode = FALSE;
$baseURI = "http://example.com/";

$pageLanguages = array("fi", "en");
$pageCSSData = array("cookie" => "lukcss", "prefix" => "luk");

$classDefaultID = "DEFAULT";
$classIDFile = "classes.txt";
$courseCacheFile = "coursecache.txt";

if (file_exists("config.inc.php"))
  @require "config.inc.php";

//
// Hardcoded tables
//
$lukDayNames = array(
  "fi" => array("Maanantai", "Tiistai", "Keskiviikko", "Torstai", "Perjantai", "Lauantai", "Sunnuntai"),
  "en" => array("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"),
);

$pageTranslations = array(
  "Original" => array("fi" => "Alkuperäinen"),

  "Shown" => array("fi" => "Näkyvillä"),
  "Next period" => array("fi" => "Seuraava periodi"),
  "Current period" => array("fi" => "Nykyinen periodi"),
  "Switch" => array("fi" => "Vaihda"),

  "contact" => array(
    "en" => "Contact <b>%1</b> or <b>%2</b>. The author can't be held responsible for any errors or inaccuracies in the data!",
    "fi" => "Yhteydenotot <b>%1</b> tai <b>%2</b>. En vastaa mahdollisista virheistä tai epätarkkuuksista tiedoissa!",
  ),
  
  "updated" => array(
    "en" => "Last updated: <b>%1</b>.",
    "fi" => "Päivitetty: <b>%1</b>.",
  ),
  
  "beta" => array(
    "en" => " NOTICE! OAMK Lukkari v%1 is still under development. There may be bugs.",
    "fi" => " HUOM! OAMK Lukkari v%1 on vielä kehityksen alla. Bugeja voi löytyä.",
  ),

  "viikossa" => array(
    "en" => "Total of <b>%1</b> hours in the week.",
    "fi" => "Viikossa yhteensä <b>%1</b> tuntia.",
  ),

  "classNotSet" => array(
    "en" => "Class not set, using default <b>%1</b>.",
    "fi" => "Luokkaa ei asetettu, käytetään vakioarvoa <b>%1</b>.",
  ),

  "classDataNotFound" => array(
    "fi" => "Luokan %1 tietoja ei löytynyt! Jos luokkakoodi on uusi, ".
    "ilmestyy se järjestelmään seuraavan päivityksen aikana. Luokkatiedot ".
    "päivitetään kerran viikossa.",
    "en" => "Data for class %1 was not found. If the class code is new, it ".
    "should appear in this system during the next update. The data is updated ".
    "once per week.",
  ),
  
  "classListNotFound" => array(
    "fi" => "Luokkien listaa ei löytynyt. Kokeile ladata sivu uudelleen hetken kuluttua.",
    "en" => "Class list not found. An update may be in progress, try reloading in a minute.",
  ),
  
  "classFormatError" => array(
    "fi" => "Virhe! Luokan täytyy olla muotoa <b>XXXnXXX</b>, käytetään vakioarvoa <b>%1</b>.",
    "en" => "Error! Class code must be of format <b>XXXnXXX</b>, using default value of <b>%1</b>.",
  ),

  "Style" => array(
    "fi" => "Tyyli",
  ),
  
  "currLukLink" => array(
    "fi" => "Linkki tähän lukujärjestykseen nykyisillä asetuksilla (kieli, jne.)",
    "en" => "Link to the this timetable with current settings (language, etc.)",
  ),
//  "" => array("fi" => ),
);


function lukGetDayName($day)
{
  global $lukDayNames, $pageLang;

  if (isset($lukDayNames[$pageLang]) && isset($lukDayNames[$pageLang][$day]))
    return $lukDayNames[$pageLang][$day];
  else
    return $lukDayNames["en"][$day];
}


function lukCheckClassID(&$id)
{
  global $classDefaultID;
  if (preg_match("#^([A-Z]{3}\d[A-Za-z0-9_]{1,6}|ccr|Ryh_[A-Z]{3}\d[A-Za-z0-9_]{1,6})$#", $id, $m))
  {
    $id = $m[1];
    return TRUE;
  } else {
    stError(cmQM("classFormatError", $classDefaultID));
    $id = $classDefaultID;
    return FALSE;
  }
}


function lukGetWeekdayFromTimestamp($stamp)
{
  $info = getdate($stamp);
  $day = $info["wday"];
  return ($day > 0) ? $day - 1 : $day + 6;
}


function lukGetHourStamp($stamp)
{
  return date("H:i", mktime(0, 0, $stamp, 0, 0, 0));
}


function lukGetHourStr($hour)
{
  global $classHourTimes;
  if (isset($classHourTimes[$hour]))
  {
    return
      "<br />".
      lukGetHourStamp($classHourTimes[$hour]["start"]).
      " - ".
      lukGetHourStamp($classHourTimes[$hour]["end"]).
      "<br /><br />";
  }
  else
    return "ERROR";
}


function lukMatchCourse($id)
{
  global $cache, $cacheDirty, $pageCharset, $pageLang, $mobileMode;

  //$uri = "http://www.oamk.fi/opiskelijalle/rakenne/opinto-opas/koulutusohjelmat/?sivu=oj&kieli=".strtoupper($pageLang)."&koodi1=".$id;
  $uri = "http://www.oamk.fi/koulutus_ja_hakeminen/opiskelu_oamkissa/opinto-opas/koulutusohjelmat/?sivu=oj_kuvaus&koodi1=".$id."&kieli=".strtoupper($pageLang);

  // Create the index
  if (!isset($cache[$id]))
    $cache[$id] = array();

  // Check if course exists in cache
  if (!isset($cache[$id][$pageLang]))
  {
    // Not cached, try to fetch data
    $cacheDirty = TRUE;
    $data = @file_get_contents($uri);
    if ($data !== FALSE)
    {
      $data = @iconv("iso-8859-15", $pageCharset, $data);
      if (preg_match("#<td class=\"smallheadercell\"><strong>(.+?)\s+(\d+)\s*(op|ECTS\s+cr)\s*</strong></td>#", $data, $m))
      {
        // Add data to cache
        $cache[$id][$pageLang] = array("desc" => $m[1], "op" => intval($m[2]));
      }
    }
  }
  
  if (isset($cache[$id]) && isset($cache[$id][$pageLang]))
  {
    if ($mobileMode)
    {
      return "<b>".chentities($cache[$id][$pageLang]["desc"])."</b>";
    }
    else
    {
       return
         "<a target=\"_blank\" title=\"".chentities($id." - ".$cache[$id][$pageLang]["op"]." op").
         "\" href=\"".chentities($uri)."\">".chentities($cache[$id][$pageLang]["desc"])."</a>";
    }
  }
  else
    return chentities($id);
}


function lukGetClassInfo($grouped, $data)
{
  if ($grouped)
  {
    $out = "<table><tr>";

    $maxCount = 0;
    foreach ($data as $col)
    {
      $out .= "<td>".lukMatchCourse($col[0])."</td>";
      if (count($col) > $maxCount)
        $maxCount = count($col);
    }
    $out .= "</tr>";

    for ($i = 1; $i < $maxCount; $i++)
    {
      $out .= "<tr>";
      foreach ($data as $col)
      {
        $out .= "<td>";
        if (isset($col[$i]))
          $out .= chentities($col[$i]);
        $out .= "</td>";
      }
      $out .= "</tr>";
    }

    return $out."</table>";
  }
  else
  {
    $out = lukMatchCourse($data[0][0])."<br />";

    for ($i = 1; $i < count($data[0]); $i++)
    {
      $out .= chentities($data[0][$i])."<br />";
    }

    return $out;
  }
}


function lukFindClass($day, $hour)
{
  global $classHourDefs, $classDayTable;
  if (isset($classDayTable[$day]))
  {
    foreach ($classDayTable[$day] as $id)
    {
      if ($hour >= $classHourDefs[$id]["start"] && 
          $hour <  $classHourDefs[$id]["start"] + $classHourDefs[$id]["hours"])
        return $id;
    }
  }
  return 0;
}


function lukReadClassFile($filename, &$mtimestamp)
{
  // Attempt to open file for reading
  if (($fp = @fopen($filename, "rb")) === false)
    return false;

  $mtimestamp = filemtime($filename);
  $mclasses = FALSE;

  // Lock file so that we do not get clashes
  if (flock($fp, LOCK_SH))
  {
    $mclasses = array();
    // Read and parse data
    while (!feof($fp))
    {
      $str = trim(fgets($fp, 128));
      if (strlen($str) > 2 && $str[0] != "#")
        $mclasses[] = $str;
    }

    // Release lock
    flock($fp, LOCK_UN);
  }

  fclose($fp);
  return $mclasses;
}


function lukPrintTimeTable($mini)
{
  global $classInfo, $classHourDefs, $classHourTimes;
  
//  $currStamp = time() + ((3 * 60) + 45) * 60;
  $currStamp = time();
  $currTime = $currStamp - mktime(0, 0, 0);
  $currDay = lukGetWeekdayFromTimestamp($currStamp);

  $nextStamp = $currStamp + 30 * 60;
  $nextTime = $nextStamp - mktime(0, 0, 0);
  
  // Create the timetable table
  if ($mini)
  {
    $out =
    "<table class=\"timetable\">".
    " <tr>\n  <th></th>".
    "  <th class=\"days\">".lukGetDayName($currDay)."</th>\n </tr>\n";

    $startDay = $currDay;
    $lastDay = $currDay + 1;
  }
  else
  {
    $out =
    "<p>".join("; ", $classInfo["general"]).
    //" [".cmQM("viikossa", $classInfo["totalHours"])."]".
    "</p>\n".
    "<table class=\"timetable\">".
    " <tr>\n  <th></th>\n";
    
    $cellWidth = $classInfo["maxDays"] > 0 ? 100 / $classInfo["maxDays"] : 15;
    if ($cellWidth > 25) $cellWidth = 25;

    for ($day = 0; $day < $classInfo["maxDays"]; $day++)
    {
      $out .=  "  <th style=\"width: ".$cellWidth."%;\" class=\"days\">".lukGetDayName($day)."</th>\n";
    }
    $out .= " </tr>\n";
    $startDay = 0;
    $lastDay = $classInfo["maxDays"];
  }


  for ($hour = $classInfo["firstHour"]; $hour < $classInfo["lastHour"]; $hour++)
  {
    $out .= " <tr>\n".
    "  <th class=\"hours\">".lukGetHourStr($hour)."</th>\n";
    for ($day = $startDay; $day < $lastDay; $day++)
    {
      $id = lukFindClass($day, $hour);
      if ($id > 0)
      {
        $class = &$classHourDefs[$id];
        if (!isset($class["set"]))
        {
          $class["set"] = TRUE;

          $nextActive = $day == $currDay && 
                      $nextTime >= $classHourTimes[$class["start"]]["start"] &&
                      $nextTime <  $classHourTimes[$class["start"] + $class["hours"] - 1]["end"];

          $isActive = $day == $currDay && 
                      $currTime >= $classHourTimes[$class["start"]]["start"] &&
                      $currTime <  $classHourTimes[$class["start"] + $class["hours"] - 1]["end"];

          $out .= "  <td rowspan=\"".$class["hours"]."\" class=\"".
          ($isActive ? "clactive " : "").
          (!$isActive && $nextActive ? "clnext " : "").
          ($class["grouped"] ? "clgrouped" : "clnormal")."\">".
          lukGetClassInfo($class["grouped"], $class["data"]).
          "<div class=\"nhours\"><span>".$class["hours"]."h</span></div></td>\n";
        }
      }
      else
      {
        $out .= "  <td class=\"clnothing\"></td>\n";
      }
    }
    $out .= " </tr>\n";
  }

  return $out."</table>\n";
}



//
// Main code begins
//
// Check given parameters:
// Language must be the first setting to be validated,
// so that the translation support works properly.
//

if (isset($_GET["lang"]))
{
  $tmp = strtolower($_GET["lang"]);
  if (in_array($tmp, $pageLanguages))
  {
    $pageLang = $tmp;
    setcookie("luklang", $tmp, time() + 365*24*60*60); // expire in a year
  }
}
else
if (isset($_COOKIE["luklang"]))
{
  $tmp = $_COOKIE["luklang"];
  if (in_array($tmp, $pageLanguages))
    $pageLang = $tmp;
}


// Check if we are using mobile mode
/*
if (isset($_GET["m"]))
{
  $mobileMode = intval($_GET["m"]);
  setcookie("lukmobile", $mobileMode, time() + 365*24*60*60); // expire in a year
}
else
if (isset($_COOKIE["lukmobile"]))
{
  $mobileMode = $_COOKIE["lukmobile"];
}

if ($mobileMode)
{
  $pageCSSData["prefix"] = $baseURI."mluk";
}
*/

// Check class setting
if (isset($_GET["luokka"]))
{
  $classID = $_GET["luokka"];
  if (lukCheckClassID($classID))
  {
    setcookie("lukluokka", $classID, time() + 365*24*60*60); // expire in a year
  }
}
else
if (isset($_COOKIE["lukluokka"]))
{
  $classID = $_COOKIE["lukluokka"];
  lukCheckClassID($classID);
}
else
{
  stError(cmQM("classNotSet", $classDefaultID));
  $classID = $classDefaultID;
}


// Check next period flag
if (isset($_GET["next"]))
{
  $nextPeriod = TRUE;
  $cachePath = "cache-next/";
}
else
{
  $nextPeriod = FALSE;
  $cachePath = "cache/";
}

if (file_exists($cachePath."baseuri.data"))
{
  require $cachePath."baseuri.data";
}


// Global cache for course data
$cache = array();
$cacheDirty = FALSE;

// Try to read cachefile, if we can get file lock on it
$fp = @fopen($courseCacheFile, "rb");
if ($fp)
{
  if (flock($fp, LOCK_SH))
  {
    require($courseCacheFile);
    flock($fp, LOCK_UN);
  }
  fclose($fp);
}


// Read classfile
if (($classIDs = lukReadClassFile($classIDFile, &$timestamp)) === false)
  stError(cmQM("classListNotFound"));


// Read class data
$dataFile = $cachePath.$classID.".data";
if (!file_exists($dataFile))
{
  stError(cmQM("classDataNotFound", chentities($classID)));
  $haveData = FALSE;
}
else
{
  require($dataFile);
  $haveData = isset($classInfo);
}


// Set some variables
$pageTitle = $haveData ? $classID." / ".join("; ", $classInfo["info"]) : $classID;


// Create references to mobile device (Apple touch) icons
$extra = "";
foreach (array(57 => FALSE, 76 => TRUE, 114 => TRUE, 120 => TRUE, 152 => TRUE) as $iconSize => $addSize)
{
  $extra .= "  <link rel=\"apple-touch-icon\" ".
    ($addSize ? "sizes=\"".$iconSize."x".$iconSize."\" " : "").
    "href=\"img/icon-".$iconSize."-precomposed.png\" />\n";
}


// Start printing the page
cmPrintPageHeader($pageTitle, $extra);


// Info box
echo
  "<div id=\"infobox\">\n".
  " <div id=\"ctitle\">OAMK Lukkari v".$pageVersion."</div>\n";

if (!$mobileMode)
{
  echo
    " <div id=\"csssel\">".cmQM("Style").": ";

  foreach ($pageCSSAlts as $name => $id)
  {
    echo
      "<a ".(($pageCSSIndex == $id) ? "class=\"selected\" " : "").
      "href=\"".$baseURI."?css=".$id."\">".$name."</a>";
  }

  echo
    "</div>\n";
}

echo
  " <div id=\"clang\">";

foreach ($pageLanguages as $id)
{
  echo
    "<a ".(($pageLang == $id) ? "class=\"selected\" " : "").
    "href=\"".$baseURI."?lang=".$id."\">".$id."</a>";
}

echo
  "</div>\n".
  "</div>\n";


// Additional controls
echo
  "<form id=\"controls\" action=\"".$baseURI."\" method=\"get\">\n".
  " <table>\n".
  "  <tr>\n".
  "   <th>\n".
  "    <select name=\"luokka\">\n";

foreach ($classIDs as $id)
{
  echo
    "     <option ".($classID == $id ? "selected=\"selected\" " : "").
    "value=\"".$id."\">".chentities($id)."</option>\n";
}

echo 
  "    </select>\n".
  "   </th>\n".
  "   <th><input class=\"submit\" type=\"submit\" value=\"".cmQM("Switch")."\" /></th>\n";

if (!$mobileMode)
{
  echo
    "   <th><a id=\"next\" class=\"textctrl\" href=\"".$baseURI.($nextPeriod ? "" : "?next")."\">".
    cmQM("Shown").": ".($nextPeriod ? cmQM("Next period") : cmQM("Current period")).
    "</a></th>\n";
  
  if ($haveData)
  {
    echo
    "   <th><a id=\"orig\" class=\"textctrl\" href=\"".$origBaseURI.$classID.$origBaseExt."\">".cmQM("Original")."</a></th>\n";
    //"   <th><a id=\"xml\" class=\"textctrl\" href=\"".$baseURI.$cachePath."/".$luokka.".xml\">XML</a></th>\n";
  }
  
  echo
    "   <th><a class=\"textctrl mobile\" href=\"http://tnsp.org/mluk/\">Mobile</a></th>\n";
}

$currURL = $baseURI."?luokka=".$classID."&amp;lang=".$pageLang.
  (isset($pageCSSIndex) ? "&amp;css=".$pageCSSIndex : "").
  ($nextPeriod ? "&amp;next" : "");

echo
  "   <th><a href=\"".$currURL."\" class=\"textctrl\" title=\"".cmQM("currLukLink")."\">Link</a></th>\n".
//  "   <th class=\"advert\"><a href=\"http://tnsp.org/u/HXh\"><img src=\"img/cell.png\" alt=\"Division Cell\" /></a></th>\n".
  "   <th class=\"advert\"><a href=\"http://tnsp.org/u/G1X\"><img src=\"img/hdd_fi.gif\" alt=\"HDD.fi\" /></a></th>\n".
  "  </tr>\n".
  " </table>\n".
  "</form>\n".
  "<h1>".$pageTitle."</h1>\n";

// Show error messages
if ($errorSet)
{
  echo "<ul>\n";
  foreach ($errorMsgs as $msg)
    echo "<li>".$msg."</li>\n";
  echo "</ul>\n";
}

if ($haveData)
{
  echo lukPrintTimeTable($mobileMode);
}

echo
  "<div id=\"footer\">".
  cmQM("contact", "ccr @ IRCNet", "ccr (at) tnsp (dot) org").
  " / ".
  cmQM("updated", strftime("%d.%m.%Y, %H:%M", $timestamp)).
  //" <div style=\"color: red;\">".cmQM("beta", $pageVersion)."</div>\n".
  "</div>\n";

cmPrintPageFooter();


// Dump the course data cache, but only if it has changed
if ($cacheDirty)
{
  $str = "<?\n\$cache = ".var_export($cache, TRUE)."\n?>";
  if (file_put_contents($courseCacheFile, $str, LOCK_EX) === FALSE)
  {
    // Can't do much anything here ..
  }
}

?>