logo
The Falcon Programming Language
A fast, easy and powerful programming language
Location: Home page >> Site sources...
User ID:
Password:
 

Source of file site_functions.fal


//==================================
// The Falcon site - functions
//

load compiler
load dbi

const default_header = "header.ftd"
const default_footer = "footer.ftd"

stdpath = ".;/usr/lib/falcon"

enum SiteConfig
   ftpdir = "/home/ftp-root"
   docdir = "/home/falcon10/newsite/project_docs"
   prjdir = "/home/falcon10/newsite/project_dl"
end

_short_ids = [
   "bf"=>"page_id=sitewiki&er;prj_id=_falcon_site&er;sid=wiki&er;pwid=Home&er;wid=Building+Falcon"
]
_SiteData = nil

//============================================
// Global shared (and exported) data.
//

// Current (exported) page ID.
page_id = ""

// An instance of the compiler. Available to submodules and
// stored here to have it created only once.
compiler = nil

// data for the currently displayed page
// Will contain a PageData instance
currentPage = nil

// Will be the user ID if logged in
userID = nil
userLevel = nil
userProjectLevel = nil

// currently working on this project id:
project_id = nil

// currently active page section
sid = nil

// Shared dbi connection
dbcon = nil

//============================================
// standard error reporting function.

function reportError( warning, error )
   warning = htmlEscape( warning )
   > "<div class='falcon_error'><br/><b>FALCON site manager</b>: ", warning, "\n"
   if error
      > "<pre>\n", error, "\n</pre>\n"
   end
   > "</div>\n"
end


//============================================
// Small shortcuts to include headers and footers

function header( name )
   if not name: name = default_header
   return include( name, "utf-8" )
end

function footer( name )
   if not name: name = default_footer
   return include( name, "utf-8" )
end

//============================================
// Creates a link to the site pages given the ID

function makeSiteLink( pid, complete )
   if strFind( pid, "ex:" ) == 0
      if complete
         return "<a href=\"" + pid[3:] + "\" target=\"_blank\">"
      end
      return pid[3:]
   end

   if complete
      str = "<a href=\""
   else
      str = ""
   end

   str += "index.ftd?page_id=" + pid
   if complete
      return str += "\">"
   end

   return str
end

//============================================
// Builts the Navigation Tree at page top
// "Home -> about -> currentlocation..."

function makeNaviTree( spanclass )

   if page_id notin _SiteData.pages
      reportError( "page_id \"" + page_id + "\" not defined." )
      return
   end

   pid = page_id
   list = []
   while true
      if isCallable( pid )
         link, pageName, pid = pid()
         list = [ [link, pageName] ] + list
      else
         page = _SiteData.pages[pid]
         pid = page.parentID
         if not isCallable( pid )
            pageName = page.pageName.typeId() == StringType ? page.pageName : page.pageName()
            list = [ [makeSiteLink(page.pageID), pageName] ] + list
         end
      end

      // is the parent ID a function -- call it and get the new page ID?
      if pid == nil: break
   end
   
   for elem in list
      formiddle
         >> "<a href=\"", elem[0], "\">", elem[1], "</a>"
         >> "&er;nbsp;>>&er;nbsp;"
      end

      forlast
         > "<span class=\"", spanclass,"\">", elem[1], "</span>"
      end
   end
end


function ParseShortId()
   if (short_id = Request.getField("s", nil))
      if short_id in _short_ids
         Reply.redirect( "/?" + _short_ids[short_id], 0 )
         Reply.commit()
         return true
      else
         raise "Unknown short id " + short_id
      end
   end

   return false
end

//============================================
// Sets the page ID and load the dynamic data
// about this page.

function setPageId( id )
   global page_id, project_id, sid, compiler, _SiteData, currentPage

   if id == nil
      id = Request.getField( "page_id", nil )
      project_id = Request.getField( "prj_id", nil )
      sid = Request.getField( "sid", nil )
   end

   if id == nil
      page_id = "Home"
   else
      page_id = id
   end

   if not compiler: compiler = Compiler()
   compiler.sourceEncoding = "utf-8"

   try
      sitedata_mod = compiler.loadByName( "sitedata" )
      siteDataMaker = sitedata_mod.get( "makeSiteData" )
      _SiteData = siteDataMaker()
   catch in error
      reportError( "Site data setup procedure failed", error )
   end

   if page_id notin _SiteData.pages
      reportError( "page_id \"" + page_id + "\" not defined." )
      return
   end

   currentPage = _SiteData.pages[page_id]
end

//============================================
// Calls the content provider of the current page

function loadContent()
   currentPage.pageProvider()
end

function loadRightCol()

>'<div class="secondary_column">'
   if currentPage.sideProvider
      currentPage.sideProvider()
   end

>'
<!-- Google Custom Search Element -->
<div id="cse" >Loading</div>
<script src="http://www.google.com/jsapi" type="text/javascript"></script>
<script type="text/javascript">
  google.load(''search'', ''1'', {style: google.loader.themes.ESPRESSO});
  google.setOnLoadCallback(function(){
    new google.search.CustomSearchControl(''011892735444422309429:qr6i2j-hwgy'').draw(''cse'');
  }, true);
</script>
<div id="discord">
<iframe src="https://discordapp.com/widget?id=401245161054535681&er;theme=dark" width="300" height="400" allowtransparency="true" frameborder="0"></iframe>
</div>
</div>
'

end

//============================================
// Performs login checks.
//

function loggedIn()
   global userId

   // if the userID is already given, then we're logged in
   if userID: return true

   // check if a userID + valid password is in the cookies.
end

function checkLogin()
   // is a userID request set?
   uID = Request.getField( "userID", nil )
   if uID
      password = Request.getField( "password", nil )
      
      try
         db = connectDB()
         query = "
            select membership from members where
            member_id=? and password=? and status='enabled'"
         r = db.query( query, uID, password )

         if (data = r.fetch([]))
            global userID, userLevel
            userID = uID
            userLevel = data[0]

            // great, but set also the cookie that allows our user to relog-in at each page.
            //if "userID" notin Request.cookies
               Reply.setCookie( "userID", userID )
               Reply.setCookie( "password", password )
            //end
         end

      catch DBIError in dbie
         > "Cannot connect with database to check login request: "
         > dbie
      end
   end

end

// ===========================================
// are we in a project?
function checkPrjRights( prid )
   global userID

   // if we're not logged, we have no rights.
   if not userID
      return nil
   end

   // a committee member? -- always rightful
   if userLevel == "inner" or userLevel == "outer"
      return "admin"
   end

   // already fetched a userProjectLevel?
   global userProjectLevel
   if userProjectLevel
      return userProjectLevel
   end

   r = connectDB().query(
      "select role from project_members where member=? and project=?",
      userID, prid )
   if ( data = r.fetch([]) )
      userProjectLevel = data[0]
   end

   return userProjectLevel
end


// ===========================================
// Performs logout and deregister cookies
//
function logout()
   global userID
   Reply.setCookie( "userID", "" )
   Reply.setCookie( "password", "" )
   userID = nil
end

//============================================
// Fulfil requests at document load
//
function checkRequest()
   req = Request.getField( "req", nil )

   switch req
      case "logout": logout()
      case "add_news": add_news()
      case "edit_news": edit_news()
   end
end

//============================================
// add a news
//
function add_news()
   project = Request.getField( "prj_id" )
   if checkPrjRights( project ) == "admin"
      con = connectDB()
      con.query( "insert into project_news " +
         "(project, news_title, news_heading, news_content, news_poster, news_posted_on)" +
         " values( ?, ?, ?, ?, ?, now())",
         project,
         Request.getField( "news_title" ),
         Request.getField( "news_heading" ),
         Request.getField( "news_content" ),
         userID
      )
   else
     reportError( @"Insufficient rights to add news to prject $(project)" )
   end
end

//============================================
// add a news
//
function edit_news()
   con = connectDB()
   // retreive rights on the current project
   nid = Request.getField( "news_id" )
   r = con.query( "select project from project_news where news_id=?", nid )
   if not ( data = r.fetch([]) )
      reportError( @"News id $(nid) not found" )
      return
   end

   // get the project
   prj_id = data[0]
   level = checkPrjRights( prj_id )
   if  level == "admin" or level == "developer"
      con = connectDB()
      con.query( "update project_news where news_id=? set " +
         "news_title=?, news_heading=?, news_content=?",
         nid,
         Request.getField( "news_title" ),
         Request.getField( "news_heading" ),
         Request.getField( "news_content" )
      )
   else
      reportError( @"Insufficient rights to edit news $(nid)" )
   end
end

function getProjectName()
   prj = Request.getField( "prj_id" )
   // get project data

   r = connectDB().query( "select name from projects where project_id=?", prj )   
   return r.fetch([])[0]
end

function getMemberName()
   try
      mid = Request.getField( "member_id" )
   catch
      return "(Invalid member)"
   end
   // get project data

   r = connectDB().query( "select name, surname from members where member_id=?", mid )
   name, surname = r.fetch([])
   return surname+ ", "+ name
end

//===========================================
// Page producer for sections (virtual pages)

function sections( sectvect )
   global sid

   if sid == nil
      sid = sectvect[0][0]
   end

   for sect in sectvect
      forfirst: > '<ul class="primary_tabs">'
      if sect[0] == sid
         cls = ' class="active"'
         sfunc = sect[2]
      else
         cls = ""
      end 

      >> @"<li$cls>"
      >> makeSiteLink( @"$(page_id)&er;amp;prj_id=$(project_id)&er;amp;sid=$(sect[0])", true)
      >> sect[1], "</a>"
      > "</li>"
      forlast: > "</ul>"
   end

   if sfunc: sfunc()
end

//===========================================
// Tree parentship producer for sections (virtual pages)

function section_tree( prj_sections )
   // get the current name and the address.
   global sid

   for sect in prj_sections
      forfirst
         // discard the first
         continue
      end

      if sect[0] == sid
         // found
         list = [ @ "?page_id=$(page_id)&er;amp;prj_id=$(project_id)&er;amp;sid=$(sid)", sect[1] ]
         break
      end
   end

   // now, if we have some other parameters, prepare next functions.
   if paramCount() > 1
      parent_call = [ sect_parent_tree ]
      for r in [1:paramCount()]: parent_call += parameter( r )

      // if we didn't found a page_id, then this is the main page;
      // return directly the parent call.
      if not list
         return parent_call()
      end
      list += [parent_call]
   else
      raise @ "sid=$sid not found and no parent calls"
      list += nil
   end

   return list
end

//======================================================
// Service function for section_tree()

function sect_parent_tree( pid )
   // locate the parent
   page = _SiteData.pages[pid]
   pageName = page.pageName.typeId() == StringType ? page.pageName : page.pageName()
   list = [makeSiteLink(page.pageID) +  @"&er;amp;prj_id=$(project_id)", pageName]
   pid = page.parentID

   // now, what else have we to send?
   if paramCount() > 1
      parent_call = [ sect_parent_tree ]
      for r in [1:paramCount()]: parent_call += parameter( r )
      list += [parent_call]
   else
      list += nil
   end

   return list
end

//============================================
// Connecting with the db.
// (throws the DB error)

function connectDB()
   global dbcon

   if dbcon: return dbcon
 
   cfgfile = InputStream("../db.config")
   data = cfgfile.grab(4096).trim()
   cfgfile.close()

   dbcon = connect(data)
   dbcon.query( 'SET NAMES latin1')
   return dbcon
end

//============================================
// Get the list of files in a directory
// dir: directory to read
// globs: array of globs to be returned.
// Returns a list of dir or nil on error

function dirList( directory, globs )
   try
      dir = Directory( directory )
      files = []

      while ( entry = dir.read() )
         if entry != "." and entry != ".."
            // check if the entry in one of the blogs
            for glob in globs
               if strWildcardMatch( entry, glob, true )
                  files += entry
               end
            end
         end
      end

      dir.close()
      return files

   catch IoError
      if dir: dir.close()
      > "<br/><b>Warning:</b> error while scanning directory ", directory
      return nil
   end
end

export
Loading

Elapsed time: 0.039 secs. (VM time 0.032 secs.)