├── .htaccess ├── README.md ├── assets ├── css │ ├── bootstrap-select.min.css │ ├── bootstrap.css │ ├── image-picker.css │ ├── jasny-bootstrap.min.css │ └── main.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── img │ └── logo.png └── js │ ├── bootstrap-maxlength.min.js │ ├── bootstrap-select.min.js │ ├── bootstrap.min.js │ ├── image-picker.min.js │ ├── jasny-bootstrap.min.js │ ├── main.js │ └── mininotification.js ├── config.php ├── controllers ├── api.php ├── connect.php ├── cron.php ├── helpers.php ├── home.php ├── oops.php ├── social.php ├── team.php └── users.php ├── db ├── 1.txt └── 2.txt ├── favicon.ico ├── images └── index.htm ├── index.php ├── libraries ├── facebook │ ├── base_facebook.php │ ├── facebook.php │ └── fb_ca_chain_bundle.crt ├── helper.class.php ├── markdown.php ├── pagination.class.php ├── postmark.class.php ├── recaptcha.php ├── shared.php ├── template.class.php ├── timeago.php ├── twitter │ ├── OAuth.php │ ├── cacert.pem │ ├── twitter.php │ └── twitter2.php └── uaparser.class.php └── views ├── connect ├── facebook.php └── index.php ├── footer.php ├── header.php ├── oops ├── noaccounts.php ├── notfound.php └── permissions.php ├── social ├── footer.php ├── header.php ├── manual.php ├── post.php ├── queue.php ├── schedule.php └── suggestions.php ├── team ├── index.php ├── invite.php ├── invited.php └── manage.php └── users ├── inform.php ├── invite.php ├── login.php └── register.php /.htaccess: -------------------------------------------------------------------------------- 1 | 2 | DirectoryIndex index.php 3 | 4 | 5 | # Turn on URL rewriting 6 | RewriteEngine On 7 | 8 | # Put your installation directory here: 9 | # If your URL is www.example.com/, use / 10 | # If your URL is www.example.com/kohana/, use /kohana/ 11 | # RewriteBase / 12 | 13 | # Do not enable rewriting for files or directories that exist 14 | RewriteCond %{REQUEST_FILENAME} !-f 15 | RewriteCond %{REQUEST_FILENAME} !-d 16 | 17 | # For reuests that are not actual files or directories, 18 | # Rewrite to index.php/URL 19 | RewriteRule ^(.*)$ index.php/$1 [PT,L] 20 | 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SocialTurn 2 | 3 | ## Installation Guide 4 | 5 | 1. Update config.php as per the values given. You will have to signup for a Facebook & Twitter App. 6 | 2. Run the SQL queries in "db" folder 7 | 3. Create a cronjob and call yoursite.com/cron/suggestions once a day and yoursite.com/cron/post every 5 minutes 8 | 4. That's all folks! 9 | -------------------------------------------------------------------------------- /assets/css/bootstrap-select.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap-select v1.5.4 (http://silviomoreto.github.io/bootstrap-select/) 3 | * 4 | * Copyright 2013-2014 bootstrap-select 5 | * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/README.md#copyright-and-license) 6 | */.bootstrap-select{width:220px \0}.bootstrap-select>.btn{width:100%;padding-right:25px}.error .bootstrap-select .btn{border:1px solid #b94a48}.bootstrap-select.fit-width{width:auto!important}.bootstrap-select.btn-group:not(.input-group-btn),.bootstrap-select.btn-group[class*=span]{float:none;display:inline-block;margin-bottom:10px;margin-left:0}.form-search .bootstrap-select.btn-group,.form-inline .bootstrap-select.btn-group,.form-horizontal .bootstrap-select.btn-group{margin-bottom:0}.bootstrap-select.form-control{margin-bottom:0;padding:0;border:none}.bootstrap-select.btn-group.pull-right,.bootstrap-select.btn-group[class*=span].pull-right,.row-fluid .bootstrap-select.btn-group[class*=span].pull-right{float:right}.input-append .bootstrap-select.btn-group{margin-left:-1px}.input-prepend .bootstrap-select.btn-group{margin-right:-1px}.bootstrap-select:not([class*=span]):not([class*=col-]):not([class*=form-control]):not(.input-group-btn){width:220px}.bootstrap-select.form-control:not([class*=span]){width:100%}.bootstrap-select.show-menu-arrow.open>.btn{z-index:2051}.bootstrap-select .btn:focus{outline:thin dotted #333!important;outline:5px auto -webkit-focus-ring-color!important;outline-offset:-2px}.bootstrap-select.btn-group .btn .filter-option{display:inline-block;overflow:hidden;width:100%;text-align:left}.bootstrap-select.btn-group .btn .caret{position:absolute;top:50%;right:12px;margin-top:-2px;vertical-align:middle}.bootstrap-select.btn-group>.disabled,.bootstrap-select.btn-group .dropdown-menu li.disabled>a{cursor:not-allowed}.bootstrap-select.btn-group>.disabled:focus{outline:0!important}.bootstrap-select.btn-group[class*=span] .btn{width:100%}.bootstrap-select.btn-group .dropdown-menu{min-width:100%;z-index:2000;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group .dropdown-menu.inner{position:static;border:0;padding:0;margin:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.bootstrap-select.btn-group .dropdown-menu dt{display:block;padding:3px 20px;cursor:default}.bootstrap-select.btn-group .div-contain{overflow:hidden}.bootstrap-select.btn-group .dropdown-menu li{position:relative}.bootstrap-select.btn-group .dropdown-menu li>a.opt{position:relative;padding-left:35px}.bootstrap-select.btn-group .dropdown-menu li>a{cursor:pointer}.bootstrap-select.btn-group .dropdown-menu li>dt small{font-weight:400}.bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a i.check-mark{position:absolute;display:inline-block;right:15px;margin-top:2.5px}.bootstrap-select.btn-group .dropdown-menu li a i.check-mark{display:none}.bootstrap-select.btn-group .dropdown-menu li a span.text{display:inline-block}.bootstrap-select.btn-group.show-tick .dropdown-menu li a span.text{margin-right:34px}.bootstrap-select.btn-group .dropdown-menu li small{padding-left:.5em}.bootstrap-select.btn-group .dropdown-menu li:not(.disabled)>a:hover small,.bootstrap-select.btn-group .dropdown-menu li:not(.disabled)>a:focus small,.bootstrap-select.btn-group .dropdown-menu li.active:not(.disabled)>a small{color:#64b1d8;color:rgba(100,177,216,.4)}.bootstrap-select.btn-group .dropdown-menu li>dt small{font-weight:400}.bootstrap-select.show-menu-arrow .dropdown-toggle:before{content:'';border-left:7px solid transparent;border-right:7px solid transparent;border-bottom-width:7px;border-bottom-style:solid;border-bottom-color:#ccc;border-bottom-color:rgba(204,204,204,.2);position:absolute;bottom:-4px;left:9px;display:none}.bootstrap-select.show-menu-arrow .dropdown-toggle:after{content:'';border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;bottom:-4px;left:10px;display:none}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:before{bottom:auto;top:-3px;border-bottom:0;border-top-width:7px;border-top-style:solid;border-top-color:#ccc;border-top-color:rgba(204,204,204,.2)}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:after{bottom:auto;top:-3px;border-top:6px solid #FFF;border-bottom:0}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:before{right:12px;left:auto}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:after{right:13px;left:auto}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:before,.bootstrap-select.show-menu-arrow.open>.dropdown-toggle:after{display:block}.bootstrap-select.btn-group .no-results{padding:3px;background:#f5f5f5;margin:0 5px}.bootstrap-select.btn-group .dropdown-menu .notify{position:absolute;bottom:5px;width:96%;margin:0 2%;min-height:26px;padding:3px 5px;background:#f5f5f5;border:1px solid #e3e3e3;box-shadow:inset 0 1px 1px rgba(0,0,0,.05);pointer-events:none;opacity:.9;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select.btn-group.fit-width .btn .filter-option{position:static}.bootstrap-select.btn-group.fit-width .btn .caret{position:static;top:auto;margin-top:-1px}.control-group.error .bootstrap-select .dropdown-toggle{border-color:#b94a48}.bootstrap-select-searchbox,.bootstrap-select .bs-actionsbox{padding:4px 8px}.bootstrap-select .bs-actionsbox{float:left;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select .bs-actionsbox .btn-group button{width:50%}.bootstrap-select-searchbox+.bs-actionsbox{padding:0 8px 4px}.bootstrap-select-searchbox input{margin-bottom:0}.mobile-device{position:absolute;top:0;left:0;display:block!important;width:100%;height:100%!important;opacity:0} -------------------------------------------------------------------------------- /assets/css/image-picker.css: -------------------------------------------------------------------------------- 1 | ul.thumbnails.image_picker_selector { 2 | overflow: auto; 3 | list-style-image: none; 4 | list-style-position: outside; 5 | list-style-type: none; 6 | padding: 0px; 7 | margin: 0px; } 8 | ul.thumbnails.image_picker_selector ul { 9 | overflow: auto; 10 | list-style-image: none; 11 | list-style-position: outside; 12 | list-style-type: none; 13 | padding: 0px; 14 | margin: 0px; } 15 | ul.thumbnails.image_picker_selector li.group_title { 16 | float: none; } 17 | ul.thumbnails.image_picker_selector li { 18 | margin: 0px 12px 12px 0px; 19 | float: left; } 20 | ul.thumbnails.image_picker_selector li .thumbnail { 21 | padding: 6px; 22 | border: 1px solid #dddddd; 23 | -webkit-user-select: none; 24 | -moz-user-select: none; 25 | -ms-user-select: none; } 26 | ul.thumbnails.image_picker_selector li .thumbnail img { 27 | -webkit-user-drag: none; } 28 | ul.thumbnails.image_picker_selector li .thumbnail.selected { 29 | background: #0088cc; } 30 | -------------------------------------------------------------------------------- /assets/css/jasny-bootstrap.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Jasny Bootstrap v3.1.3 (http://jasny.github.io/bootstrap) 3 | * Copyright 2012-2014 Arnold Daniels 4 | * Licensed under Apache-2.0 (https://github.com/jasny/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | .container-smooth{max-width:1170px}@media (min-width:1px){.container-smooth{width:auto}}.btn-labeled{padding-top:0;padding-bottom:0}.btn-label{position:relative;background:0 0;background:rgba(0,0,0,.15);display:inline-block;padding:6px 12px;left:-12px;border-radius:3px 0 0 3px}.btn-label.btn-label-right{left:auto;right:-12px;border-radius:0 3px 3px 0}.btn-lg .btn-label{padding:10px 16px;left:-16px;border-radius:5px 0 0 5px}.btn-lg .btn-label.btn-label-right{left:auto;right:-16px;border-radius:0 5px 5px 0}.btn-sm .btn-label{padding:5px 10px;left:-10px;border-radius:2px 0 0 2px}.btn-sm .btn-label.btn-label-right{left:auto;right:-10px;border-radius:0 2px 2px 0}.btn-xs .btn-label{padding:1px 5px;left:-5px;border-radius:2px 0 0 2px}.btn-xs .btn-label.btn-label-right{left:auto;right:-5px;border-radius:0 2px 2px 0}.nav-tabs-bottom{border-bottom:0;border-top:1px solid #ddd}.nav-tabs-bottom>li{margin-bottom:0;margin-top:-1px}.nav-tabs-bottom>li>a{border-radius:0 0 4px 4px}.nav-tabs-bottom>li>a:hover,.nav-tabs-bottom>li>a:focus,.nav-tabs-bottom>li.active>a,.nav-tabs-bottom>li.active>a:hover,.nav-tabs-bottom>li.active>a:focus{border:1px solid #ddd;border-top-color:transparent}.nav-tabs-left{border-bottom:0;border-right:1px solid #ddd}.nav-tabs-left>li{margin-bottom:0;margin-right:-1px;float:none}.nav-tabs-left>li>a{border-radius:4px 0 0 4px;margin-right:0;margin-bottom:2px}.nav-tabs-left>li>a:hover,.nav-tabs-left>li>a:focus,.nav-tabs-left>li.active>a,.nav-tabs-left>li.active>a:hover,.nav-tabs-left>li.active>a:focus{border:1px solid #ddd;border-right-color:transparent}.row>.nav-tabs-left{padding-right:0;padding-left:15px;margin-right:-1px;position:relative;z-index:1}.row>.nav-tabs-left+.tab-content{border-left:1px solid #ddd}.nav-tabs-right{border-bottom:0;border-left:1px solid #ddd}.nav-tabs-right>li{margin-bottom:0;margin-left:-1px;float:none}.nav-tabs-right>li>a{border-radius:0 4px 4px 0;margin-left:0;margin-bottom:2px}.nav-tabs-right>li>a:hover,.nav-tabs-right>li>a:focus,.nav-tabs-right>li.active>a,.nav-tabs-right>li.active>a:hover,.nav-tabs-right>li.active>a:focus{border:1px solid #ddd;border-left-color:transparent}.row>.nav-tabs-right{padding-left:0;padding-right:15px}.navmenu,.navbar-offcanvas{width:300px;height:auto;border-width:1px;border-style:solid;border-radius:4px}.navmenu-fixed-left,.navmenu-fixed-right,.navbar-offcanvas{position:fixed;z-index:1030;top:0;bottom:0;overflow-y:auto;border-radius:0}.navmenu-fixed-left,.navbar-offcanvas.navmenu-fixed-left{left:0;right:auto;border-width:0 1px 0 0}.navmenu-fixed-right,.navbar-offcanvas{left:auto;right:0;border-width:0 0 0 1px}.navmenu-nav{margin-bottom:10px}.navmenu-nav.dropdown-menu{position:static;margin:0;padding-top:0;float:none;border:none;-webkit-box-shadow:none;box-shadow:none;border-radius:0}.navbar-offcanvas .navbar-nav{margin:0}@media (min-width:768px){.navbar-offcanvas{width:auto;border-top:0;box-shadow:none}.navbar-offcanvas.offcanvas{position:static;display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-offcanvas .navbar-nav.navbar-left:first-child{margin-left:-15px}.navbar-offcanvas .navbar-nav.navbar-right:last-child{margin-right:-15px}.navbar-offcanvas .navmenu-brand{display:none}}.navmenu-brand{display:block;font-size:18px;line-height:20px;padding:10px 15px;margin:10px 0}.navmenu-brand:hover,.navmenu-brand:focus{text-decoration:none}.navmenu-default,.navbar-default .navbar-offcanvas{background-color:#f8f8f8;border-color:#e7e7e7}.navmenu-default .navmenu-brand,.navbar-default .navbar-offcanvas .navmenu-brand{color:#777}.navmenu-default .navmenu-brand:hover,.navbar-default .navbar-offcanvas .navmenu-brand:hover,.navmenu-default .navmenu-brand:focus,.navbar-default .navbar-offcanvas .navmenu-brand:focus{color:#5e5e5e;background-color:transparent}.navmenu-default .navmenu-text,.navbar-default .navbar-offcanvas .navmenu-text{color:#777}.navmenu-default .navmenu-nav>.dropdown>a:hover .caret,.navbar-default .navbar-offcanvas .navmenu-nav>.dropdown>a:hover .caret,.navmenu-default .navmenu-nav>.dropdown>a:focus .caret,.navbar-default .navbar-offcanvas .navmenu-nav>.dropdown>a:focus .caret{border-top-color:#333;border-bottom-color:#333}.navmenu-default .navmenu-nav>.open>a,.navbar-default .navbar-offcanvas .navmenu-nav>.open>a,.navmenu-default .navmenu-nav>.open>a:hover,.navbar-default .navbar-offcanvas .navmenu-nav>.open>a:hover,.navmenu-default .navmenu-nav>.open>a:focus,.navbar-default .navbar-offcanvas .navmenu-nav>.open>a:focus{background-color:#e7e7e7;color:#555}.navmenu-default .navmenu-nav>.open>a .caret,.navbar-default .navbar-offcanvas .navmenu-nav>.open>a .caret,.navmenu-default .navmenu-nav>.open>a:hover .caret,.navbar-default .navbar-offcanvas .navmenu-nav>.open>a:hover .caret,.navmenu-default .navmenu-nav>.open>a:focus .caret,.navbar-default .navbar-offcanvas .navmenu-nav>.open>a:focus .caret{border-top-color:#555;border-bottom-color:#555}.navmenu-default .navmenu-nav>.dropdown>a .caret,.navbar-default .navbar-offcanvas .navmenu-nav>.dropdown>a .caret{border-top-color:#777;border-bottom-color:#777}.navmenu-default .navmenu-nav.dropdown-menu,.navbar-default .navbar-offcanvas .navmenu-nav.dropdown-menu{background-color:#e7e7e7}.navmenu-default .navmenu-nav.dropdown-menu>.divider,.navbar-default .navbar-offcanvas .navmenu-nav.dropdown-menu>.divider{background-color:#f8f8f8}.navmenu-default .navmenu-nav.dropdown-menu>.active>a,.navbar-default .navbar-offcanvas .navmenu-nav.dropdown-menu>.active>a,.navmenu-default .navmenu-nav.dropdown-menu>.active>a:hover,.navbar-default .navbar-offcanvas .navmenu-nav.dropdown-menu>.active>a:hover,.navmenu-default .navmenu-nav.dropdown-menu>.active>a:focus,.navbar-default .navbar-offcanvas .navmenu-nav.dropdown-menu>.active>a:focus{background-color:#d7d7d7}.navmenu-default .navmenu-nav>li>a,.navbar-default .navbar-offcanvas .navmenu-nav>li>a{color:#777}.navmenu-default .navmenu-nav>li>a:hover,.navbar-default .navbar-offcanvas .navmenu-nav>li>a:hover,.navmenu-default .navmenu-nav>li>a:focus,.navbar-default .navbar-offcanvas .navmenu-nav>li>a:focus{color:#333;background-color:transparent}.navmenu-default .navmenu-nav>.active>a,.navbar-default .navbar-offcanvas .navmenu-nav>.active>a,.navmenu-default .navmenu-nav>.active>a:hover,.navbar-default .navbar-offcanvas .navmenu-nav>.active>a:hover,.navmenu-default .navmenu-nav>.active>a:focus,.navbar-default .navbar-offcanvas .navmenu-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navmenu-default .navmenu-nav>.disabled>a,.navbar-default .navbar-offcanvas .navmenu-nav>.disabled>a,.navmenu-default .navmenu-nav>.disabled>a:hover,.navbar-default .navbar-offcanvas .navmenu-nav>.disabled>a:hover,.navmenu-default .navmenu-nav>.disabled>a:focus,.navbar-default .navbar-offcanvas .navmenu-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navmenu-inverse,.navbar-inverse .navbar-offcanvas{background-color:#222;border-color:#080808}.navmenu-inverse .navmenu-brand,.navbar-inverse .navbar-offcanvas .navmenu-brand{color:#999}.navmenu-inverse .navmenu-brand:hover,.navbar-inverse .navbar-offcanvas .navmenu-brand:hover,.navmenu-inverse .navmenu-brand:focus,.navbar-inverse .navbar-offcanvas .navmenu-brand:focus{color:#fff;background-color:transparent}.navmenu-inverse .navmenu-text,.navbar-inverse .navbar-offcanvas .navmenu-text{color:#999}.navmenu-inverse .navmenu-nav>.dropdown>a:hover .caret,.navbar-inverse .navbar-offcanvas .navmenu-nav>.dropdown>a:hover .caret,.navmenu-inverse .navmenu-nav>.dropdown>a:focus .caret,.navbar-inverse .navbar-offcanvas .navmenu-nav>.dropdown>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}.navmenu-inverse .navmenu-nav>.open>a,.navbar-inverse .navbar-offcanvas .navmenu-nav>.open>a,.navmenu-inverse .navmenu-nav>.open>a:hover,.navbar-inverse .navbar-offcanvas .navmenu-nav>.open>a:hover,.navmenu-inverse .navmenu-nav>.open>a:focus,.navbar-inverse .navbar-offcanvas .navmenu-nav>.open>a:focus{background-color:#080808;color:#fff}.navmenu-inverse .navmenu-nav>.open>a .caret,.navbar-inverse .navbar-offcanvas .navmenu-nav>.open>a .caret,.navmenu-inverse .navmenu-nav>.open>a:hover .caret,.navbar-inverse .navbar-offcanvas .navmenu-nav>.open>a:hover .caret,.navmenu-inverse .navmenu-nav>.open>a:focus .caret,.navbar-inverse .navbar-offcanvas .navmenu-nav>.open>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}.navmenu-inverse .navmenu-nav>.dropdown>a .caret,.navbar-inverse .navbar-offcanvas .navmenu-nav>.dropdown>a .caret{border-top-color:#999;border-bottom-color:#999}.navmenu-inverse .navmenu-nav.dropdown-menu,.navbar-inverse .navbar-offcanvas .navmenu-nav.dropdown-menu{background-color:#080808}.navmenu-inverse .navmenu-nav.dropdown-menu>.divider,.navbar-inverse .navbar-offcanvas .navmenu-nav.dropdown-menu>.divider{background-color:#222}.navmenu-inverse .navmenu-nav.dropdown-menu>.active>a,.navbar-inverse .navbar-offcanvas .navmenu-nav.dropdown-menu>.active>a,.navmenu-inverse .navmenu-nav.dropdown-menu>.active>a:hover,.navbar-inverse .navbar-offcanvas .navmenu-nav.dropdown-menu>.active>a:hover,.navmenu-inverse .navmenu-nav.dropdown-menu>.active>a:focus,.navbar-inverse .navbar-offcanvas .navmenu-nav.dropdown-menu>.active>a:focus{background-color:#000}.navmenu-inverse .navmenu-nav>li>a,.navbar-inverse .navbar-offcanvas .navmenu-nav>li>a{color:#999}.navmenu-inverse .navmenu-nav>li>a:hover,.navbar-inverse .navbar-offcanvas .navmenu-nav>li>a:hover,.navmenu-inverse .navmenu-nav>li>a:focus,.navbar-inverse .navbar-offcanvas .navmenu-nav>li>a:focus{color:#fff;background-color:transparent}.navmenu-inverse .navmenu-nav>.active>a,.navbar-inverse .navbar-offcanvas .navmenu-nav>.active>a,.navmenu-inverse .navmenu-nav>.active>a:hover,.navbar-inverse .navbar-offcanvas .navmenu-nav>.active>a:hover,.navmenu-inverse .navmenu-nav>.active>a:focus,.navbar-inverse .navbar-offcanvas .navmenu-nav>.active>a:focus{color:#fff;background-color:#080808}.navmenu-inverse .navmenu-nav>.disabled>a,.navbar-inverse .navbar-offcanvas .navmenu-nav>.disabled>a,.navmenu-inverse .navmenu-nav>.disabled>a:hover,.navbar-inverse .navbar-offcanvas .navmenu-nav>.disabled>a:hover,.navmenu-inverse .navmenu-nav>.disabled>a:focus,.navbar-inverse .navbar-offcanvas .navmenu-nav>.disabled>a:focus{color:#444;background-color:transparent}.alert-fixed-top,.alert-fixed-bottom{position:fixed;width:100%;z-index:1035;border-radius:0;margin:0;left:0}@media (min-width:992px){.alert-fixed-top,.alert-fixed-bottom{width:992px;left:50%;margin-left:-496px}}.alert-fixed-top{top:0;border-width:0 0 1px}@media (min-width:992px){.alert-fixed-top{border-bottom-right-radius:4px;border-bottom-left-radius:4px;border-width:0 1px 1px}}.alert-fixed-bottom{bottom:0;border-width:1px 0 0}@media (min-width:992px){.alert-fixed-bottom{border-top-right-radius:4px;border-top-left-radius:4px;border-width:1px 1px 0}}.offcanvas{display:none}.offcanvas.in{display:block}@media (max-width:767px){.offcanvas-xs{display:none}.offcanvas-xs.in{display:block}}@media (max-width:991px){.offcanvas-sm{display:none}.offcanvas-sm.in{display:block}}@media (max-width:1199px){.offcanvas-md{display:none}.offcanvas-md.in{display:block}}.offcanvas-lg{display:none}.offcanvas-lg.in{display:block}.canvas-sliding{-webkit-transition:top .35s,left .35s,bottom .35s,right .35s;transition:top .35s,left .35s,bottom .35s,right .35s}.offcanvas-clone{height:0!important;width:0!important;overflow:hidden!important;border:none!important;margin:0!important;padding:0!important;position:absolute!important;top:auto!important;left:auto!important;bottom:0!important;right:0!important;opacity:0!important}.table.rowlink td:not(.rowlink-skip),.table .rowlink td:not(.rowlink-skip){cursor:pointer}.table.rowlink td:not(.rowlink-skip) a,.table .rowlink td:not(.rowlink-skip) a{color:inherit;font:inherit;text-decoration:inherit}.table-hover.rowlink tr:hover td,.table-hover .rowlink tr:hover td{background-color:#cfcfcf}.btn-file{overflow:hidden;position:relative;vertical-align:middle}.btn-file>input{position:absolute;top:0;right:0;margin:0;opacity:0;filter:alpha(opacity=0);font-size:23px;height:100%;width:100%;direction:ltr;cursor:pointer}.fileinput{margin-bottom:9px;display:inline-block}.fileinput .form-control{padding-top:7px;padding-bottom:5px;display:inline-block;margin-bottom:0;vertical-align:middle;cursor:text}.fileinput .thumbnail{overflow:hidden;display:inline-block;margin-bottom:5px;vertical-align:middle;text-align:center}.fileinput .thumbnail>img{max-height:100%}.fileinput .btn{vertical-align:middle}.fileinput-exists .fileinput-new,.fileinput-new .fileinput-exists{display:none}.fileinput-inline .fileinput-controls{display:inline}.fileinput-filename{vertical-align:middle;display:inline-block;overflow:hidden}.form-control .fileinput-filename{vertical-align:bottom}.fileinput.input-group{display:table}.fileinput.input-group>*{position:relative;z-index:2}.fileinput.input-group>.btn-file{z-index:1}.fileinput-new.input-group .btn-file,.fileinput-new .input-group .btn-file{border-radius:0 4px 4px 0}.fileinput-new.input-group .btn-file.btn-xs,.fileinput-new .input-group .btn-file.btn-xs,.fileinput-new.input-group .btn-file.btn-sm,.fileinput-new .input-group .btn-file.btn-sm{border-radius:0 3px 3px 0}.fileinput-new.input-group .btn-file.btn-lg,.fileinput-new .input-group .btn-file.btn-lg{border-radius:0 6px 6px 0}.form-group.has-warning .fileinput .fileinput-preview{color:#8a6d3b}.form-group.has-warning .fileinput .thumbnail{border-color:#faebcc}.form-group.has-error .fileinput .fileinput-preview{color:#a94442}.form-group.has-error .fileinput .thumbnail{border-color:#ebccd1}.form-group.has-success .fileinput .fileinput-preview{color:#3c763d}.form-group.has-success .fileinput .thumbnail{border-color:#d6e9c6}.input-group-addon:not(:first-child){border-left:0} -------------------------------------------------------------------------------- /assets/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Carlos Alvarez 3 | * URL: http://alvarez.is 4 | * 5 | * Project Name: FLATTY - Free Bootstrap 3 Theme 6 | * Version: 1.0 7 | * URL: http://blacktie.co 8 | */ 9 | 10 | body { 11 | background-color: #f2f2f2; 12 | font-family: 'Lato', sans-serif; 13 | font-weight: 300; 14 | font-size: 16px; 15 | color: #555; 16 | 17 | -webkit-font-smoothing: antialiased; 18 | -webkit-overflow-scrolling: touch; 19 | } 20 | 21 | /* Titles */ 22 | h1, h2, h3, h4, h5, h6 { 23 | font-family: 'Lato', sans-serif; 24 | font-weight: 300; 25 | color: #333; 26 | } 27 | 28 | h1 { 29 | font-size: 40px; 30 | } 31 | 32 | h3 { 33 | color: #95a5a6; 34 | font-weight: 400; 35 | } 36 | 37 | h4 { 38 | color: #95a5a6; 39 | font-weight: 400; 40 | font-size: 20px; 41 | } 42 | 43 | /* Paragraph & Typographic */ 44 | p { 45 | line-height: 28px; 46 | margin-bottom: 25px; 47 | font-size: 16px; 48 | } 49 | 50 | .centered { 51 | text-align: center; 52 | } 53 | 54 | /* Links */ 55 | a { 56 | color: #3498db; 57 | word-wrap: break-word; 58 | 59 | -webkit-transition: color 0.1s ease-in, background 0.1s ease-in; 60 | -moz-transition: color 0.1s ease-in, background 0.1s ease-in; 61 | -ms-transition: color 0.1s ease-in, background 0.1s ease-in; 62 | -o-transition: color 0.1s ease-in, background 0.1s ease-in; 63 | transition: color 0.1s ease-in, background 0.1s ease-in; 64 | } 65 | 66 | a:hover, 67 | a:focus { 68 | color: #7b7b7b; 69 | text-decoration: none; 70 | outline: 0; 71 | } 72 | 73 | a:before, 74 | a:after { 75 | -webkit-transition: color 0.1s ease-in, background 0.1s ease-in; 76 | -moz-transition: color 0.1s ease-in, background 0.1s ease-in; 77 | -ms-transition: color 0.1s ease-in, background 0.1s ease-in; 78 | -o-transition: color 0.1s ease-in, background 0.1s ease-in; 79 | transition: color 0.1s ease-in, background 0.1s ease-in; 80 | } 81 | 82 | hr { 83 | display: block; 84 | height: 1px; 85 | border: 0; 86 | border-top: 1px solid #ccc; 87 | margin: 1em 0; 88 | padding: 0; 89 | } 90 | 91 | .navbar-default { 92 | background-color: #ffffff; 93 | border-color: transparent; 94 | border-bottom: 1px solid #ccc; 95 | height: 80px; 96 | } 97 | 98 | .navbar-default .navbar-brand { 99 | color: black; 100 | margin-top: 3px; 101 | font-size: 26px; 102 | font-weight: normal; 103 | } 104 | 105 | .navbar-default .navbar-nav > li > a { 106 | margin-top: 15px; 107 | 108 | font-family: "Open Sans",Helvetica,Arial,sans-serif; 109 | font-size: 14px; 110 | font-weight: 400; 111 | } 112 | 113 | /* Helpers */ 114 | 115 | .mt { 116 | margin-top: 40px; 117 | margin-bottom: 40px; 118 | } 119 | 120 | .form-control { 121 | height: 42px; 122 | font-size: 18px; 123 | width: 280px; 124 | } 125 | 126 | textarea { 127 | width: 100% !important; 128 | } 129 | 130 | i { 131 | margin: 8px; 132 | color: #3498db; 133 | } 134 | 135 | 136 | /* HeaderWrap */ 137 | #headerwrap { 138 | /* background: url(../img/bg01.jpg) no-repeat center top; */ 139 | background-color: #ffffff; 140 | margin-top: -20px; 141 | padding-top:200px; 142 | background-attachment: relative; 143 | background-position: center center; 144 | min-height: 650px; 145 | width: 100%; 146 | 147 | -webkit-background-size: 100%; 148 | -moz-background-size: 100%; 149 | -o-background-size: 100%; 150 | background-size: 100%; 151 | 152 | -webkit-background-size: cover; 153 | -moz-background-size: cover; 154 | -o-background-size: cover; 155 | background-size: cover; 156 | } 157 | 158 | #headerwrap h1 { 159 | margin-top: 60px; 160 | margin-bottom: 15px; 161 | color: black; 162 | font-size: 45px; 163 | font-weight: 300; 164 | letter-spacing: 1px; 165 | } 166 | 167 | /* New */ 168 | 169 | body { 170 | font-family: "Open Sans",Helvetica,Arial,sans-serif; 171 | font-size: 14px; 172 | font-weight: 400; 173 | line-height: 1.42857143; 174 | color: #8b959e; 175 | background: #ffffff; 176 | } 177 | 178 | h1, h2, h3, h4, h5, h6 { 179 | font-family: inherit; 180 | font-weight: 600; 181 | line-height: 1.4; 182 | color: #323b43; 183 | margin: 30px 0 20px; 184 | } 185 | 186 | h2 { 187 | font-size: 40px; 188 | font-family: inherit; 189 | font-weight: 600; 190 | line-height: 1.4; 191 | color: #323b43; 192 | margin: 30px 0 20px; 193 | } 194 | 195 | h5 { 196 | font-size: 18px; 197 | } 198 | 199 | h4 { 200 | margin: 30px 0 10px; 201 | } 202 | 203 | .spacer { 204 | margin-top: 40px; 205 | } 206 | 207 | .spacer-sm { 208 | margin-top: 20px; 209 | } 210 | 211 | .spacer-xs { 212 | margin-top: 10px; 213 | } 214 | 215 | .btn-border { 216 | border: 2px solid #aaa; 217 | background: white; 218 | } 219 | 220 | .social_image { 221 | border-radius: 4px; 222 | -webkit-background-clip: padding-box; 223 | background-clip: padding-box; 224 | } 225 | 226 | .social_image_base { 227 | position: relative; 228 | width: 50px; 229 | height: 50px; 230 | } 231 | 232 | .social_image_base2 { 233 | position: relative; 234 | width: 64px; 235 | height: 72px; 236 | } 237 | 238 | .social_image_superimpose { 239 | position: absolute; 240 | right: 1px; 241 | bottom: 1px; 242 | } 243 | 244 | .social_image_superimpose2 { 245 | position: absolute; 246 | right: 1px; 247 | bottom: 8px; 248 | } 249 | 250 | .connect_facebook h5 { 251 | margin: 0px; 252 | } 253 | 254 | .connect_facebook span { 255 | margin-top: 5px; 256 | color: #8b959e; 257 | text-decoration: none; 258 | } 259 | 260 | .team_index h5, .social_queue h5, .connect_index h5 { 261 | margin: 0px; 262 | } 263 | 264 | .team_index span { 265 | margin-top: 5px; 266 | color: #8b959e; 267 | text-decoration: none; 268 | } 269 | 270 | .connect_facebook .page, .connect_index .page { 271 | padding: 12px; 272 | } 273 | 274 | .team-index .page { 275 | padding: 12px; 276 | } 277 | 278 | .connect_facebook .page:hover { 279 | background: #eeeeee; 280 | } 281 | 282 | .social_index .social_image_base { 283 | float: left; 284 | margin-right: 15px; 285 | } 286 | 287 | .team-manage .social_image_base { 288 | float: left; 289 | margin-right: 15px; 290 | } 291 | 292 | .google-plus { 293 | color: #dd4b39; 294 | } 295 | 296 | .twitter { 297 | color: #4099ff; 298 | } 299 | 300 | .facebook { 301 | color: #3b5998; 302 | } 303 | 304 | .users .form-control { 305 | display: inline; 306 | } 307 | 308 | .image-picker { 309 | display: none; 310 | } 311 | 312 | a .btn-border { 313 | color: #666; 314 | } 315 | 316 | .logo { 317 | color: #0085c3; 318 | } 319 | 320 | .btn-primary { 321 | background-color: #0085c3; 322 | } 323 | 324 | .navbar-brand { 325 | color: #0085c3 !important; 326 | } 327 | 328 | .thumbnail { 329 | background-color: #eeeeee; 330 | border: 1px solid #cccccc; 331 | } 332 | 333 | .navbar-default .navbar-nav > .active > a, 334 | .navbar-default .navbar-nav > .active > a:hover, 335 | .navbar-default .navbar-nav > .active > a:focus { 336 | color: #555555; 337 | background-color: #ffffff; 338 | font-weight: 700; 339 | } 340 | 341 | .nav-image { 342 | width: 12px; 343 | height: 12px; 344 | margin: 8px 0; 345 | margin-top: 8px; 346 | margin-bottom: 10px; 347 | border-radius: 2px; 348 | } 349 | 350 | /* miniNotification jQuery plugin */ 351 | body { 352 | font-family: helvetica,'Georgia',arial,sans-serif; 353 | background-color: #fff; 354 | color: #3C3C3C; 355 | } 356 | 357 | html { 358 | overflow: -moz-scrollbars-vertical; 359 | overflow: scroll-y; 360 | } 361 | 362 | p { 363 | padding: 10px 0; 364 | margin: 0; 365 | } 366 | 367 | h1 { 368 | font-size: 24px; 369 | line-height: 48px; 370 | font-weight: 100; 371 | letter-spacing: 1px; 372 | border-bottom: 1px solid #3C3C3C; 373 | } 374 | 375 | h1 strong { 376 | text-transform: uppercase; 377 | font-weight: bold; 378 | } 379 | 380 | #main { 381 | width: 980px; 382 | margin: 40px auto; 383 | } 384 | 385 | #mini-notification { 386 | display: none; 387 | position: fixed; 388 | cursor: pointer; 389 | width: 100%; 390 | background: #fff; 391 | font-size: 14px; 392 | text-align: center; 393 | border-top: 2px solid #fff; 394 | z-index:9999; 395 | color: #3C3C3C; 396 | -moz-box-shadow:0 0em 0.5em rgba(0, 0, 0, 0.3); 397 | -webkit-box-shadow:0 0em 0.5em rgba(0, 0, 0, 0.3); 398 | box-shadow:0 0em 0.5em rgba(0, 0, 0, 0.3); 399 | } 400 | 401 | #mini-notification .inner { 402 | position: relative; 403 | width: 800px; 404 | margin: 0 auto; 405 | padding-right: 60px; 406 | } 407 | 408 | 409 | #mini-notification .close { 410 | position: absolute; 411 | color: #000; 412 | top: 10px; 413 | right: 0; 414 | } 415 | 416 | #mini-notification a.close:hover { 417 | text-decoration: underline; 418 | } 419 | 420 | #notification { 421 | display: none; 422 | position: fixed; 423 | cursor: pointer; 424 | width: 100%; 425 | text-align: center; 426 | z-index:9999; 427 | } 428 | 429 | .notification_error { 430 | color: #a94442; 431 | border-bottom: 1px solid #ebccd1; 432 | background-color: #f2dede; 433 | } 434 | 435 | 436 | .notification_success { 437 | color: #3c763d; 438 | border-bottom: 1px solid #d6e9c6; 439 | background-color: #dff0d8; 440 | } 441 | 442 | .social_queue .scheduled { 443 | color: #aaa; 444 | clear: both; 445 | } 446 | 447 | .social_suggestions .scheduled { 448 | color: #aaa; 449 | clear: both; 450 | } 451 | 452 | .social_suggestions .queue { 453 | border-bottom: 1px solid #ccc; 454 | padding-bottom: 10px; 455 | margin-bottom: 10px; 456 | } 457 | 458 | .pull-right { 459 | text-align: right; 460 | } 461 | 462 | .post { 463 | border: 1px solid #ccc; 464 | background: white; 465 | border-radius: 10px; 466 | } 467 | 468 | .post .media { 469 | height: 200px; 470 | border: 1px solid #ccc; 471 | margin-top: 10px; 472 | margin-bottom: 10px; 473 | } 474 | 475 | .post h5 { 476 | margin-top: 20px; 477 | font-size: 16px; 478 | } 479 | 480 | .post .category { 481 | margin-bottom: 20px; 482 | } -------------------------------------------------------------------------------- /assets/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anantgarg/socialturn/a48c16cfcca89df4e5398698af39b14bc9b7de75/assets/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /assets/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anantgarg/socialturn/a48c16cfcca89df4e5398698af39b14bc9b7de75/assets/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /assets/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anantgarg/socialturn/a48c16cfcca89df4e5398698af39b14bc9b7de75/assets/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anantgarg/socialturn/a48c16cfcca89df4e5398698af39b14bc9b7de75/assets/img/logo.png -------------------------------------------------------------------------------- /assets/js/bootstrap-maxlength.min.js: -------------------------------------------------------------------------------- 1 | /* ========================================================== 2 | * 3 | * bootstrap-maxlength.js v 1.5.5 4 | * Copyright 2014 Maurizio Napoleoni @mimonap 5 | * Licensed under MIT License 6 | * URL: https://github.com/mimo84/bootstrap-maxlength/blob/master/LICENSE 7 | * 8 | * ========================================================== */ 9 | 10 | !function(a){"use strict";a.event.special.destroyed||(a.event.special.destroyed={remove:function(a){a.handler&&a.handler()}}),a.fn.extend({maxlength:function(b,c){function d(a){var c=a.val();c=c.replace(new RegExp("\r?\n","g"),"\n");var d=0;return d=b.utf8?f(a.val()):a.val().length}function e(a,b){var c=a.val();a.val(c.substr(0,b))}function f(a){for(var b=0,c=0;cd?b++:b+=d>127&&2048>d?2:3}return b}function g(a,c,e){var f=!0;return!b.alwaysShow&&e-d(a)>c&&(f=!1),f}function h(a,b){var c=b-d(a);return c}function i(a){a.css({display:"block"})}function j(a){a.css({display:"none"})}function k(a,c){var d="";return b.message?d=b.message.replace("%charsTyped%",c).replace("%charsRemaining%",a-c).replace("%charsTotal%",a):(b.preText&&(d+=b.preText),d+=b.showCharsTyped?c:a-c,b.showMaxLength&&(d+=b.separator+a),b.postText&&(d+=b.postText)),d}function l(a,c,d,e){e.html(k(d,d-a)),a>0?g(c,b.threshold,d)?i(e.removeClass(b.limitReachedClass).addClass(b.warningClass)):j(e):i(e.removeClass(b.warningClass).addClass(b.limitReachedClass))}function m(b){var c=b[0];return a.extend({},"function"==typeof c.getBoundingClientRect?c.getBoundingClientRect():{width:c.offsetWidth,height:c.offsetHeight},b.offset())}function n(a,c){var d=m(a),e=a.outerWidth(),f=c.outerWidth(),g=c.width(),h=c.height();switch(b.appendToParent&&(d.top-=a.parent().offset().top,d.left-=a.parent().offset().left),b.placement){case"bottom":c.css({top:d.top+d.height,left:d.left+d.width/2-g/2});break;case"top":c.css({top:d.top-h,left:d.left+d.width/2-g/2});break;case"left":c.css({top:d.top+d.height/2-h/2,left:d.left-g});break;case"right":c.css({top:d.top+d.height/2-h/2,left:d.left+d.width});break;case"bottom-right":c.css({top:d.top+d.height,left:d.left+d.width});break;case"top-right":c.css({top:d.top-h,left:d.left+e});break;case"top-left":c.css({top:d.top-h,left:d.left-f});break;case"bottom-left":c.css({top:d.top+a.outerHeight(),left:d.left-f});break;case"centered-right":c.css({top:d.top+h/2,left:d.left+e-f-3});break;case"bottom-right-inside":c.css({top:d.top+d.height,left:d.left+d.width-f});break;case"top-right-inside":c.css({top:d.top-h,left:d.left+e-f});break;case"top-left-inside":c.css({top:d.top-h,left:d.left});break;case"bottom-left-inside":c.css({top:d.top+a.outerHeight(),left:d.left})}}function o(a){return a.attr("maxlength")||a.attr("size")}var p=a("body"),q={showOnReady:!1,alwaysShow:!1,threshold:10,warningClass:"label label-success",limitReachedClass:"label label-important label-danger",separator:" / ",preText:"",postText:"",showMaxLength:!0,placement:"bottom",showCharsTyped:!0,validate:!1,utf8:!1,appendToParent:!1};return a.isFunction(b)&&!c&&(c=b,b={}),b=a.extend(q,b),this.each(function(){function c(){var c=k(d,"0");d=o(g),f||(f=a('').css({display:"none",position:"absolute",whiteSpace:"nowrap",zIndex:1099}).html(c)),g.is("textarea")&&(g.data("maxlenghtsizex",g.outerWidth()),g.data("maxlenghtsizey",g.outerHeight()),g.mouseup(function(){(g.outerWidth()!==g.data("maxlenghtsizex")||g.outerHeight()!==g.data("maxlenghtsizey"))&&n(g,f),g.data("maxlenghtsizex",g.outerWidth()),g.data("maxlenghtsizey",g.outerHeight())})),b.appendToParent?(g.parent().append(f),g.parent().css("position","relative")):p.append(f);var e=h(g,o(g));l(e,g,d,f),n(g,f)}var d,f,g=a(this);a(window).resize(function(){f&&n(g,f)}),b.showOnReady?g.ready(function(){c()}):g.focus(function(){c()}),g.on("destroyed",function(){f&&f.remove()}),g.on("blur",function(){f&&!b.showOnReady&&f.remove()}),g.on("input",function(){var a=o(g),c=h(g,a),i=!0;return b.validate&&0>c?(e(g,a),i=!1):l(c,g,d,f),("bottom-right-inside"===b.placement||"top-right-inside"===b.placement)&&n(g,f),i})})}})}(jQuery); -------------------------------------------------------------------------------- /assets/js/image-picker.min.js: -------------------------------------------------------------------------------- 1 | // Image Picker 2 | // by Rodrigo Vera 3 | // 4 | // Version 0.2.4 5 | // Full source at https://github.com/rvera/image-picker 6 | // MIT License, https://github.com/rvera/image-picker/blob/master/LICENSE 7 | // Generated by CoffeeScript 1.4.0 8 | (function() { 9 | var ImagePicker, ImagePickerOption, both_array_are_equal, sanitized_options, 10 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, 11 | __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; 12 | 13 | jQuery.fn.extend({ 14 | imagepicker: function(opts) { 15 | if (opts == null) { 16 | opts = {}; 17 | } 18 | return this.each(function() { 19 | var select; 20 | select = jQuery(this); 21 | if (select.data("picker")) { 22 | select.data("picker").destroy(); 23 | } 24 | select.data("picker", new ImagePicker(this, sanitized_options(opts))); 25 | if (opts.initialized != null) { 26 | return opts.initialized.call(select.data("picker")); 27 | } 28 | }); 29 | } 30 | }); 31 | 32 | sanitized_options = function(opts) { 33 | var default_options; 34 | default_options = { 35 | hide_select: true, 36 | show_label: false, 37 | initialized: void 0, 38 | changed: void 0, 39 | clicked: void 0, 40 | selected: void 0, 41 | limit: void 0, 42 | limit_reached: void 0 43 | }; 44 | return jQuery.extend(default_options, opts); 45 | }; 46 | 47 | both_array_are_equal = function(a, b) { 48 | return jQuery(a).not(b).length === 0 && jQuery(b).not(a).length === 0; 49 | }; 50 | 51 | ImagePicker = (function() { 52 | 53 | function ImagePicker(select_element, opts) { 54 | this.opts = opts != null ? opts : {}; 55 | this.sync_picker_with_select = __bind(this.sync_picker_with_select, this); 56 | 57 | this.select = jQuery(select_element); 58 | this.multiple = this.select.attr("multiple") === "multiple"; 59 | if (this.select.data("limit") != null) { 60 | this.opts.limit = parseInt(this.select.data("limit")); 61 | } 62 | this.build_and_append_picker(); 63 | } 64 | 65 | ImagePicker.prototype.destroy = function() { 66 | var option, _i, _len, _ref; 67 | _ref = this.picker_options; 68 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 69 | option = _ref[_i]; 70 | option.destroy(); 71 | } 72 | this.picker.remove(); 73 | this.select.unbind("change"); 74 | this.select.removeData("picker"); 75 | return this.select.show(); 76 | }; 77 | 78 | ImagePicker.prototype.build_and_append_picker = function() { 79 | var _this = this; 80 | if (this.opts.hide_select) { 81 | this.select.hide(); 82 | } 83 | this.select.change(function() { 84 | return _this.sync_picker_with_select(); 85 | }); 86 | if (this.picker != null) { 87 | this.picker.remove(); 88 | } 89 | this.create_picker(); 90 | this.select.after(this.picker); 91 | return this.sync_picker_with_select(); 92 | }; 93 | 94 | ImagePicker.prototype.sync_picker_with_select = function() { 95 | var option, _i, _len, _ref, _results; 96 | _ref = this.picker_options; 97 | _results = []; 98 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 99 | option = _ref[_i]; 100 | if (option.is_selected()) { 101 | _results.push(option.mark_as_selected()); 102 | } else { 103 | _results.push(option.unmark_as_selected()); 104 | } 105 | } 106 | return _results; 107 | }; 108 | 109 | ImagePicker.prototype.create_picker = function() { 110 | this.picker = jQuery(""); 111 | this.picker_options = []; 112 | this.recursively_parse_option_groups(this.select, this.picker); 113 | return this.picker; 114 | }; 115 | 116 | ImagePicker.prototype.recursively_parse_option_groups = function(scoped_dom, target_container) { 117 | var container, option, option_group, _i, _j, _len, _len1, _ref, _ref1, _results; 118 | _ref = scoped_dom.children("optgroup"); 119 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 120 | option_group = _ref[_i]; 121 | option_group = jQuery(option_group); 122 | container = jQuery(""); 123 | container.append(jQuery("
  • " + (option_group.attr("label")) + "
  • ")); 124 | target_container.append(jQuery("
  • ").append(container)); 125 | this.recursively_parse_option_groups(option_group, container); 126 | } 127 | _ref1 = (function() { 128 | var _k, _len1, _ref1, _results1; 129 | _ref1 = scoped_dom.children("option"); 130 | _results1 = []; 131 | for (_k = 0, _len1 = _ref1.length; _k < _len1; _k++) { 132 | option = _ref1[_k]; 133 | _results1.push(new ImagePickerOption(option, this, this.opts)); 134 | } 135 | return _results1; 136 | }).call(this); 137 | _results = []; 138 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { 139 | option = _ref1[_j]; 140 | this.picker_options.push(option); 141 | if (!option.has_image()) { 142 | continue; 143 | } 144 | _results.push(target_container.append(option.node)); 145 | } 146 | return _results; 147 | }; 148 | 149 | ImagePicker.prototype.has_implicit_blanks = function() { 150 | var option; 151 | return ((function() { 152 | var _i, _len, _ref, _results; 153 | _ref = this.picker_options; 154 | _results = []; 155 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 156 | option = _ref[_i]; 157 | if (option.is_blank() && !option.has_image()) { 158 | _results.push(option); 159 | } 160 | } 161 | return _results; 162 | }).call(this)).length > 0; 163 | }; 164 | 165 | ImagePicker.prototype.selected_values = function() { 166 | if (this.multiple) { 167 | return this.select.val() || []; 168 | } else { 169 | return [this.select.val()]; 170 | } 171 | }; 172 | 173 | ImagePicker.prototype.toggle = function(imagepicker_option) { 174 | var new_values, old_values, selected_value; 175 | old_values = this.selected_values(); 176 | selected_value = imagepicker_option.value().toString(); 177 | if (this.multiple) { 178 | if (__indexOf.call(this.selected_values(), selected_value) >= 0) { 179 | new_values = this.selected_values(); 180 | new_values.splice(jQuery.inArray(selected_value, old_values), 1); 181 | this.select.val([]); 182 | this.select.val(new_values); 183 | } else { 184 | if ((this.opts.limit != null) && this.selected_values().length >= this.opts.limit) { 185 | if (this.opts.limit_reached != null) { 186 | this.opts.limit_reached.call(this.select); 187 | } 188 | } else { 189 | this.select.val(this.selected_values().concat(selected_value)); 190 | } 191 | } 192 | } else { 193 | if (this.has_implicit_blanks() && imagepicker_option.is_selected()) { 194 | this.select.val(""); 195 | } else { 196 | this.select.val(selected_value); 197 | } 198 | } 199 | if (!both_array_are_equal(old_values, this.selected_values())) { 200 | this.select.change(); 201 | if (this.opts.changed != null) { 202 | return this.opts.changed.call(this.select, old_values, this.selected_values()); 203 | } 204 | } 205 | }; 206 | 207 | return ImagePicker; 208 | 209 | })(); 210 | 211 | ImagePickerOption = (function() { 212 | 213 | function ImagePickerOption(option_element, picker, opts) { 214 | this.picker = picker; 215 | this.opts = opts != null ? opts : {}; 216 | this.clicked = __bind(this.clicked, this); 217 | 218 | this.option = jQuery(option_element); 219 | this.create_node(); 220 | } 221 | 222 | ImagePickerOption.prototype.destroy = function() { 223 | return this.node.find(".thumbnail").unbind(); 224 | }; 225 | 226 | ImagePickerOption.prototype.has_image = function() { 227 | return this.option.data("img-src") != null; 228 | }; 229 | 230 | ImagePickerOption.prototype.is_blank = function() { 231 | return !((this.value() != null) && this.value() !== ""); 232 | }; 233 | 234 | ImagePickerOption.prototype.is_selected = function() { 235 | var select_value; 236 | select_value = this.picker.select.val(); 237 | if (this.picker.multiple) { 238 | return jQuery.inArray(this.value(), select_value) >= 0; 239 | } else { 240 | return this.value() === select_value; 241 | } 242 | }; 243 | 244 | ImagePickerOption.prototype.mark_as_selected = function() { 245 | return this.node.find(".thumbnail").addClass("selected"); 246 | }; 247 | 248 | ImagePickerOption.prototype.unmark_as_selected = function() { 249 | return this.node.find(".thumbnail").removeClass("selected"); 250 | }; 251 | 252 | ImagePickerOption.prototype.value = function() { 253 | return this.option.val(); 254 | }; 255 | 256 | ImagePickerOption.prototype.label = function() { 257 | if (this.option.data("img-label")) { 258 | return this.option.data("img-label"); 259 | } else { 260 | return this.option.text(); 261 | } 262 | }; 263 | 264 | ImagePickerOption.prototype.clicked = function() { 265 | this.picker.toggle(this); 266 | if (this.opts.clicked != null) { 267 | this.opts.clicked.call(this.picker.select, this); 268 | } 269 | if ((this.opts.selected != null) && this.is_selected()) { 270 | return this.opts.selected.call(this.picker.select, this); 271 | } 272 | }; 273 | 274 | ImagePickerOption.prototype.create_node = function() { 275 | var image, thumbnail; 276 | this.node = jQuery("
  • "); 277 | image = jQuery(""); 278 | image.attr("src", this.option.data("img-src")); 279 | image.attr("title", this.option.data("img-label")); 280 | 281 | thumbnail = jQuery("
    "); 282 | thumbnail.click({ 283 | option: this 284 | }, function(event) { 285 | return event.data.option.clicked(); 286 | }); 287 | thumbnail.append(image); 288 | if (this.opts.show_label) { 289 | thumbnail.append(jQuery("

    ").html(this.label())); 290 | } 291 | this.node.append(thumbnail); 292 | this.node.append(''); 293 | 294 | return this.node; 295 | }; 296 | 297 | return ImagePickerOption; 298 | 299 | })(); 300 | 301 | }).call(this); 302 | -------------------------------------------------------------------------------- /assets/js/jasny-bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Jasny Bootstrap v3.1.3 (http://jasny.github.io/bootstrap) 3 | * Copyright 2012-2014 Arnold Daniels 4 | * Licensed under Apache-2.0 (https://github.com/jasny/bootstrap/blob/master/LICENSE) 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Jasny Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}void 0===a.support.transition&&(a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()}))}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.state=null,this.placement=null,this.options.recalc&&(this.calcClone(),a(window).on("resize",a.proxy(this.recalc,this))),this.options.autohide&&a(document).on("click",a.proxy(this.autohide,this)),this.options.toggle&&this.toggle(),this.options.disablescrolling&&(this.options.disableScrolling=this.options.disablescrolling,delete this.options.disablescrolling)};b.DEFAULTS={toggle:!0,placement:"auto",autohide:!0,recalc:!0,disableScrolling:!0},b.prototype.offset=function(){switch(this.placement){case"left":case"right":return this.$element.outerWidth();case"top":case"bottom":return this.$element.outerHeight()}},b.prototype.calcPlacement=function(){function b(a,b){if("auto"===e.css(b))return a;if("auto"===e.css(a))return b;var c=parseInt(e.css(a),10),d=parseInt(e.css(b),10);return c>d?b:a}if("auto"!==this.options.placement)return void(this.placement=this.options.placement);this.$element.hasClass("in")||this.$element.css("visiblity","hidden !important").addClass("in");var c=a(window).width()/this.$element.width(),d=a(window).height()/this.$element.height(),e=this.$element;this.placement=c>=d?b("left","right"):b("top","bottom"),"hidden !important"===this.$element.css("visibility")&&this.$element.removeClass("in").css("visiblity","")},b.prototype.opposite=function(a){switch(a){case"top":return"bottom";case"left":return"right";case"bottom":return"top";case"right":return"left"}},b.prototype.getCanvasElements=function(){var b=this.options.canvas?a(this.options.canvas):this.$element,c=b.find("*").filter(function(){return"fixed"===a(this).css("position")}).not(this.options.exclude);return b.add(c)},b.prototype.slide=function(b,c,d){if(!a.support.transition){var e={};return e[this.placement]="+="+c,b.animate(e,350,d)}var f=this.placement,g=this.opposite(f);b.each(function(){"auto"!==a(this).css(f)&&a(this).css(f,(parseInt(a(this).css(f),10)||0)+c),"auto"!==a(this).css(g)&&a(this).css(g,(parseInt(a(this).css(g),10)||0)-c)}),this.$element.one(a.support.transition.end,d).emulateTransitionEnd(350)},b.prototype.disableScrolling=function(){var b=a("body").width(),c="padding-"+this.opposite(this.placement);if(void 0===a("body").data("offcanvas-style")&&a("body").data("offcanvas-style",a("body").attr("style")||""),a("body").css("overflow","hidden"),a("body").width()>b){var d=parseInt(a("body").css(c),10)+a("body").width()-b;setTimeout(function(){a("body").css(c,d)},1)}},b.prototype.show=function(){if(!this.state){var b=a.Event("show.bs.offcanvas");if(this.$element.trigger(b),!b.isDefaultPrevented()){this.state="slide-in",this.calcPlacement();var c=this.getCanvasElements(),d=this.placement,e=this.opposite(d),f=this.offset();-1!==c.index(this.$element)&&(a(this.$element).data("offcanvas-style",a(this.$element).attr("style")||""),this.$element.css(d,-1*f),this.$element.css(d)),c.addClass("canvas-sliding").each(function(){void 0===a(this).data("offcanvas-style")&&a(this).data("offcanvas-style",a(this).attr("style")||""),"static"===a(this).css("position")&&a(this).css("position","relative"),"auto"!==a(this).css(d)&&"0px"!==a(this).css(d)||"auto"!==a(this).css(e)&&"0px"!==a(this).css(e)||a(this).css(d,0)}),this.options.disableScrolling&&this.disableScrolling();var g=function(){"slide-in"==this.state&&(this.state="slid",c.removeClass("canvas-sliding").addClass("canvas-slid"),this.$element.trigger("shown.bs.offcanvas"))};setTimeout(a.proxy(function(){this.$element.addClass("in"),this.slide(c,f,a.proxy(g,this))},this),1)}}},b.prototype.hide=function(){if("slid"===this.state){var b=a.Event("hide.bs.offcanvas");if(this.$element.trigger(b),!b.isDefaultPrevented()){this.state="slide-out";var c=a(".canvas-slid"),d=(this.placement,-1*this.offset()),e=function(){"slide-out"==this.state&&(this.state=null,this.placement=null,this.$element.removeClass("in"),c.removeClass("canvas-sliding"),c.add(this.$element).add("body").each(function(){a(this).attr("style",a(this).data("offcanvas-style")).removeData("offcanvas-style")}),this.$element.trigger("hidden.bs.offcanvas"))};c.removeClass("canvas-slid").addClass("canvas-sliding"),setTimeout(a.proxy(function(){this.slide(c,d,a.proxy(e,this))},this),1)}}},b.prototype.toggle=function(){"slide-in"!==this.state&&"slide-out"!==this.state&&this["slid"===this.state?"hide":"show"]()},b.prototype.calcClone=function(){this.$calcClone=this.$element.clone().html("").addClass("offcanvas-clone").removeClass("in").appendTo(a("body"))},b.prototype.recalc=function(){if("none"!==this.$calcClone.css("display")&&("slid"===this.state||"slide-in"===this.state)){this.state=null,this.placement=null;var b=this.getCanvasElements();this.$element.removeClass("in"),b.removeClass("canvas-slid"),b.add(this.$element).add("body").each(function(){a(this).attr("style",a(this).data("offcanvas-style")).removeData("offcanvas-style")})}},b.prototype.autohide=function(b){0===a(b.target).closest(this.$element).length&&this.hide()};var c=a.fn.offcanvas;a.fn.offcanvas=function(c){return this.each(function(){var d=a(this),e=d.data("bs.offcanvas"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.offcanvas",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.offcanvas.Constructor=b,a.fn.offcanvas.noConflict=function(){return a.fn.offcanvas=c,this},a(document).on("click.bs.offcanvas.data-api","[data-toggle=offcanvas]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.offcanvas"),h=g?"toggle":d.data();b.stopPropagation(),g?g.toggle():f.offcanvas(h)})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.$element.on("click.bs.rowlink","td:not(.rowlink-skip)",a.proxy(this.click,this))};b.DEFAULTS={target:"a"},b.prototype.click=function(b){var c=a(b.currentTarget).closest("tr").find(this.options.target)[0];if(a(b.target)[0]!==c)if(b.preventDefault(),c.click)c.click();else if(document.createEvent){var d=document.createEvent("MouseEvents");d.initMouseEvent("click",!0,!0,window,0,0,0,0,0,!1,!1,!1,!1,0,null),c.dispatchEvent(d)}};var c=a.fn.rowlink;a.fn.rowlink=function(c){return this.each(function(){var d=a(this),e=d.data("bs.rowlink");e||d.data("bs.rowlink",e=new b(this,c))})},a.fn.rowlink.Constructor=b,a.fn.rowlink.noConflict=function(){return a.fn.rowlink=c,this},a(document).on("click.bs.rowlink.data-api",'[data-link="row"]',function(b){if(0===a(b.target).closest(".rowlink-skip").length){var c=a(this);c.data("bs.rowlink")||(c.rowlink(c.data()),a(b.target).trigger("click.bs.rowlink"))}})}(window.jQuery),+function(a){"use strict";var b=void 0!==window.orientation,c=navigator.userAgent.toLowerCase().indexOf("android")>-1,d="Microsoft Internet Explorer"==window.navigator.appName,e=function(b,d){c||(this.$element=a(b),this.options=a.extend({},e.DEFAULTS,d),this.mask=String(this.options.mask),this.init(),this.listen(),this.checkVal())};e.DEFAULTS={mask:"",placeholder:"_",definitions:{9:"[0-9]",a:"[A-Za-z]",w:"[A-Za-z0-9]","*":"."}},e.prototype.init=function(){var b=this.options.definitions,c=this.mask.length;this.tests=[],this.partialPosition=this.mask.length,this.firstNonMaskPos=null,a.each(this.mask.split(""),a.proxy(function(a,d){"?"==d?(c--,this.partialPosition=a):b[d]?(this.tests.push(new RegExp(b[d])),null===this.firstNonMaskPos&&(this.firstNonMaskPos=this.tests.length-1)):this.tests.push(null)},this)),this.buffer=a.map(this.mask.split(""),a.proxy(function(a){return"?"!=a?b[a]?this.options.placeholder:a:void 0},this)),this.focusText=this.$element.val(),this.$element.data("rawMaskFn",a.proxy(function(){return a.map(this.buffer,function(a,b){return this.tests[b]&&a!=this.options.placeholder?a:null}).join("")},this))},e.prototype.listen=function(){if(!this.$element.attr("readonly")){var b=(d?"paste":"input")+".mask";this.$element.on("unmask.bs.inputmask",a.proxy(this.unmask,this)).on("focus.bs.inputmask",a.proxy(this.focusEvent,this)).on("blur.bs.inputmask",a.proxy(this.blurEvent,this)).on("keydown.bs.inputmask",a.proxy(this.keydownEvent,this)).on("keypress.bs.inputmask",a.proxy(this.keypressEvent,this)).on(b,a.proxy(this.pasteEvent,this))}},e.prototype.caret=function(a,b){if(0!==this.$element.length){if("number"==typeof a)return b="number"==typeof b?b:a,this.$element.each(function(){if(this.setSelectionRange)this.setSelectionRange(a,b);else if(this.createTextRange){var c=this.createTextRange();c.collapse(!0),c.moveEnd("character",b),c.moveStart("character",a),c.select()}});if(this.$element[0].setSelectionRange)a=this.$element[0].selectionStart,b=this.$element[0].selectionEnd;else if(document.selection&&document.selection.createRange){var c=document.selection.createRange();a=0-c.duplicate().moveStart("character",-1e5),b=a+c.text.length}return{begin:a,end:b}}},e.prototype.seekNext=function(a){for(var b=this.mask.length;++a<=b&&!this.tests[a];);return a},e.prototype.seekPrev=function(a){for(;--a>=0&&!this.tests[a];);return a},e.prototype.shiftL=function(a,b){var c=this.mask.length;if(!(0>a)){for(var d=a,e=this.seekNext(b);c>d;d++)if(this.tests[d]){if(!(c>e&&this.tests[d].test(this.buffer[e])))break;this.buffer[d]=this.buffer[e],this.buffer[e]=this.options.placeholder,e=this.seekNext(e)}this.writeBuffer(),this.caret(Math.max(this.firstNonMaskPos,a))}},e.prototype.shiftR=function(a){for(var b=this.mask.length,c=a,d=this.options.placeholder;b>c;c++)if(this.tests[c]){var e=this.seekNext(c),f=this.buffer[c];if(this.buffer[c]=d,!(b>e&&this.tests[e].test(f)))break;d=f}},e.prototype.unmask=function(){this.$element.unbind(".mask").removeData("inputmask")},e.prototype.focusEvent=function(){this.focusText=this.$element.val();var a=this.mask.length,b=this.checkVal();this.writeBuffer();var c=this,d=function(){b==a?c.caret(0,b):c.caret(b)};d(),setTimeout(d,50)},e.prototype.blurEvent=function(){this.checkVal(),this.$element.val()!==this.focusText&&this.$element.trigger("change")},e.prototype.keydownEvent=function(a){var c=a.which;if(8==c||46==c||b&&127==c){var d=this.caret(),e=d.begin,f=d.end;return f-e===0&&(e=46!=c?this.seekPrev(e):f=this.seekNext(e-1),f=46==c?this.seekNext(f):f),this.clearBuffer(e,f),this.shiftL(e,f-1),!1}return 27==c?(this.$element.val(this.focusText),this.caret(0,this.checkVal()),!1):void 0},e.prototype.keypressEvent=function(a){var b=this.mask.length,c=a.which,d=this.caret();if(a.ctrlKey||a.altKey||a.metaKey||32>c)return!0;if(c){d.end-d.begin!==0&&(this.clearBuffer(d.begin,d.end),this.shiftL(d.begin,d.end-1));var e=this.seekNext(d.begin-1);if(b>e){var f=String.fromCharCode(c);if(this.tests[e].test(f)){this.shiftR(e),this.buffer[e]=f,this.writeBuffer();var g=this.seekNext(e);this.caret(g)}}return!1}},e.prototype.pasteEvent=function(){var a=this;setTimeout(function(){a.caret(a.checkVal(!0))},0)},e.prototype.clearBuffer=function(a,b){for(var c=this.mask.length,d=a;b>d&&c>d;d++)this.tests[d]&&(this.buffer[d]=this.options.placeholder)},e.prototype.writeBuffer=function(){return this.$element.val(this.buffer.join("")).val()},e.prototype.checkVal=function(a){for(var b=this.mask.length,c=this.$element.val(),d=-1,e=0,f=0;b>e;e++)if(this.tests[e]){for(this.buffer[e]=this.options.placeholder;f++c.length)break}else this.buffer[e]==c.charAt(f)&&e!=this.partialPosition&&(f++,d=e);return!a&&d+1=this.partialPosition)&&(this.writeBuffer(),a||this.$element.val(this.$element.val().substring(0,d+1))),this.partialPosition?e:this.firstNonMaskPos};var f=a.fn.inputmask;a.fn.inputmask=function(b){return this.each(function(){var c=a(this),d=c.data("bs.inputmask");d||c.data("bs.inputmask",d=new e(this,b))})},a.fn.inputmask.Constructor=e,a.fn.inputmask.noConflict=function(){return a.fn.inputmask=f,this},a(document).on("focus.bs.inputmask.data-api","[data-mask]",function(){var b=a(this);b.data("bs.inputmask")||b.inputmask(b.data())})}(window.jQuery),+function(a){"use strict";var b="Microsoft Internet Explorer"==window.navigator.appName,c=function(b,c){if(this.$element=a(b),this.$input=this.$element.find(":file"),0!==this.$input.length){this.name=this.$input.attr("name")||c.name,this.$hidden=this.$element.find('input[type=hidden][name="'+this.name+'"]'),0===this.$hidden.length&&(this.$hidden=a('').insertBefore(this.$input)),this.$preview=this.$element.find(".fileinput-preview");var d=this.$preview.css("height");"inline"!==this.$preview.css("display")&&"0px"!==d&&"none"!==d&&this.$preview.css("line-height",d),this.original={exists:this.$element.hasClass("fileinput-exists"),preview:this.$preview.html(),hiddenVal:this.$hidden.val()},this.listen()}};c.prototype.listen=function(){this.$input.on("change.bs.fileinput",a.proxy(this.change,this)),a(this.$input[0].form).on("reset.bs.fileinput",a.proxy(this.reset,this)),this.$element.find('[data-trigger="fileinput"]').on("click.bs.fileinput",a.proxy(this.trigger,this)),this.$element.find('[data-dismiss="fileinput"]').on("click.bs.fileinput",a.proxy(this.clear,this))},c.prototype.change=function(b){var c=void 0===b.target.files?b.target&&b.target.value?[{name:b.target.value.replace(/^.+\\/,"")}]:[]:b.target.files;if(b.stopPropagation(),0===c.length)return void this.clear();this.$hidden.val(""),this.$hidden.attr("name",""),this.$input.attr("name",this.name);var d=c[0];if(this.$preview.length>0&&("undefined"!=typeof d.type?d.type.match(/^image\/(gif|png|jpeg)$/):d.name.match(/\.(gif|png|jpe?g)$/i))&&"undefined"!=typeof FileReader){var e=new FileReader,f=this.$preview,g=this.$element;e.onload=function(b){var e=a("");e[0].src=b.target.result,c[0].result=b.target.result,g.find(".fileinput-filename").text(d.name),"none"!=f.css("max-height")&&e.css("max-height",parseInt(f.css("max-height"),10)-parseInt(f.css("padding-top"),10)-parseInt(f.css("padding-bottom"),10)-parseInt(f.css("border-top"),10)-parseInt(f.css("border-bottom"),10)),f.html(e),g.addClass("fileinput-exists").removeClass("fileinput-new"),g.trigger("change.bs.fileinput",c)},e.readAsDataURL(d)}else this.$element.find(".fileinput-filename").text(d.name),this.$preview.text(d.name),this.$element.addClass("fileinput-exists").removeClass("fileinput-new"),this.$element.trigger("change.bs.fileinput")},c.prototype.clear=function(a){if(a&&a.preventDefault(),this.$hidden.val(""),this.$hidden.attr("name",this.name),this.$input.attr("name",""),b){var c=this.$input.clone(!0);this.$input.after(c),this.$input.remove(),this.$input=c}else this.$input.val("");this.$preview.html(""),this.$element.find(".fileinput-filename").text(""),this.$element.addClass("fileinput-new").removeClass("fileinput-exists"),void 0!==a&&(this.$input.trigger("change"),this.$element.trigger("clear.bs.fileinput"))},c.prototype.reset=function(){this.clear(),this.$hidden.val(this.original.hiddenVal),this.$preview.html(this.original.preview),this.$element.find(".fileinput-filename").text(""),this.original.exists?this.$element.addClass("fileinput-exists").removeClass("fileinput-new"):this.$element.addClass("fileinput-new").removeClass("fileinput-exists"),this.$element.trigger("reset.bs.fileinput")},c.prototype.trigger=function(a){this.$input.trigger("click"),a.preventDefault()};var d=a.fn.fileinput;a.fn.fileinput=function(b){return this.each(function(){var d=a(this),e=d.data("bs.fileinput");e||d.data("bs.fileinput",e=new c(this,b)),"string"==typeof b&&e[b]()})},a.fn.fileinput.Constructor=c,a.fn.fileinput.noConflict=function(){return a.fn.fileinput=d,this},a(document).on("click.fileinput.data-api",'[data-provides="fileinput"]',function(b){var c=a(this);if(!c.data("bs.fileinput")){c.fileinput(c.data());var d=a(b.target).closest('[data-dismiss="fileinput"],[data-trigger="fileinput"]');d.length>0&&(b.preventDefault(),d.trigger("click.bs.fileinput"))}})}(window.jQuery); -------------------------------------------------------------------------------- /assets/js/main.js: -------------------------------------------------------------------------------- 1 | var checkConfirm = 0; 2 | 3 | $('.confirm').on('click', function() { 4 | checkConfirm = 1; 5 | }); 6 | 7 | $('form').on('submit', function() { 8 | if (checkConfirm == 1) { 9 | checkConfirm = 0; 10 | return confirm('Are you sure you want to proceed?'); 11 | } 12 | }); 13 | 14 | $('.confirmLink').on('click', function(e) { 15 | var check = confirm('Are you sure you want to proceed?'); 16 | if (check){ } else { 17 | e.stopPropagation(); 18 | e.preventDefault(); 19 | } 20 | }); -------------------------------------------------------------------------------- /assets/js/mininotification.js: -------------------------------------------------------------------------------- 1 | 2 | $(function() { 3 | $.miniNotification = function(element, options) { 4 | var appendCloseButton, getHiddenCssProps, getVisibleCssProps, setState, state, wrapInnerElement, 5 | _this = this; 6 | this.defaults = { 7 | position: 'top', 8 | show: true, 9 | effect: 'slide', 10 | opacity: 0.95, 11 | time: 4000, 12 | showSpeed: 600, 13 | hideSpeed: 450, 14 | showEasing: '', 15 | hideEasing: '', 16 | innerDivClass: 'inner', 17 | closeButton: false, 18 | closeButtonText: 'close', 19 | closeButtonClass: 'close', 20 | hideOnClick: true, 21 | onLoad: function() {}, 22 | onVisible: function() {}, 23 | onHide: function() {}, 24 | onHidden: function() {} 25 | }; 26 | state = ''; 27 | this.settings = {}; 28 | this.$element = $(element); 29 | setState = function(_state) { 30 | return state = _state; 31 | }; 32 | getHiddenCssProps = function() { 33 | var css, position; 34 | position = (_this.getSetting('effect')) === 'slide' ? 0 - _this.$element.outerHeight() : 0; 35 | css = {}; 36 | if ((_this.getSetting('position')) === 'bottom') { 37 | css['bottom'] = position; 38 | } else { 39 | css['top'] = position; 40 | } 41 | if ((_this.getSetting('effect')) === 'fade') { 42 | css['opacity'] = 0; 43 | } 44 | return css; 45 | }; 46 | getVisibleCssProps = function() { 47 | var css; 48 | css = { 49 | 'opacity': _this.getSetting('opacity') 50 | }; 51 | if ((_this.getSetting('position')) === 'bottom') { 52 | css['bottom'] = 0; 53 | } else { 54 | css['top'] = 0; 55 | } 56 | return css; 57 | }; 58 | wrapInnerElement = function() { 59 | _this.$elementInner = $('

    ', { 60 | 'class': _this.getSetting('innerDivClass') 61 | }); 62 | return _this.$element.wrapInner(_this.$elementInner); 63 | }; 64 | appendCloseButton = function() { 65 | var $closeButton; 66 | $closeButton = $('', { 67 | 'class': _this.getSetting('closeButtonClass'), 68 | 'html': _this.getSetting('closeButtonText') 69 | }); 70 | _this.$element.children().append($closeButton); 71 | return $closeButton.bind('click', function() { 72 | return _this.hide(); 73 | }); 74 | }; 75 | this.getState = function() { 76 | return state; 77 | }; 78 | this.getSetting = function(settingKey) { 79 | return this.settings[settingKey]; 80 | }; 81 | this.callSettingFunction = function(functionName) { 82 | return this.settings[functionName](element); 83 | }; 84 | this.init = function() { 85 | var _this = this; 86 | setState('hidden'); 87 | this.settings = $.extend({}, this.defaults, options); 88 | if (this.$element.length) { 89 | wrapInnerElement(); 90 | if (this.getSetting('closeButton')) { 91 | appendCloseButton(); 92 | } 93 | this.$element.css(getHiddenCssProps()).css({ 94 | display: 'inline' 95 | }); 96 | if (this.getSetting('show')) { 97 | this.show(); 98 | } 99 | if (this.getSetting('hideOnClick')) { 100 | return this.$element.bind('click', function() { 101 | if (_this.getState() !== 'hiding') { 102 | return _this.hide(); 103 | } 104 | }); 105 | } 106 | } 107 | }; 108 | this.show = function() { 109 | var _this = this; 110 | if (this.getState() !== 'showing' && this.getState() !== 'visible') { 111 | setState('showing'); 112 | this.callSettingFunction('onLoad'); 113 | return this.$element.animate(getVisibleCssProps(), this.getSetting('showSpeed'), this.getSetting('showEasing'), function() { 114 | setState('visible'); 115 | _this.callSettingFunction('onVisible'); 116 | return setTimeout((function() { 117 | return _this.hide(); 118 | }), _this.settings.time); 119 | }); 120 | } 121 | }; 122 | this.hide = function() { 123 | var _this = this; 124 | if (this.getState() !== 'hiding' && this.getState() !== 'hidden') { 125 | setState('hiding'); 126 | this.callSettingFunction('onHide'); 127 | return this.$element.animate(getHiddenCssProps(), this.getSetting('hideSpeed'), this.getSetting('hideEasing'), function() { 128 | setState('hidden'); 129 | return _this.callSettingFunction('onHidden'); 130 | }); 131 | } 132 | }; 133 | this.init(); 134 | return this; 135 | }; 136 | return $.fn.miniNotification = function(options) { 137 | return this.each(function() { 138 | var plugin; 139 | plugin = ($(this)).data('miniNotification'); 140 | if (plugin === void 0) { 141 | plugin = new $.miniNotification(this, options); 142 | return ($(this)).data('miniNotification', plugin); 143 | } else { 144 | return plugin.show(); 145 | } 146 | }); 147 | }; 148 | }); 149 | -------------------------------------------------------------------------------- /config.php: -------------------------------------------------------------------------------- 1 | parse($ua); 35 | 36 | // Add entry into track 37 | 38 | 39 | 40 | $_REQUEST['track_time'] = time(); 41 | $_REQUEST['ip'] = $_SERVER['REMOTE_ADDR']; 42 | 43 | sql('track'); 44 | 45 | 46 | // Add entry into user 47 | 48 | sql('user'); 49 | 50 | exit; 51 | } 52 | 53 | function sql($table) { 54 | 55 | global $dbh; 56 | global $database; 57 | 58 | $data = array(); 59 | 60 | foreach ($database[$table] as $field => $type) { 61 | if (!empty($_REQUEST[$field])) { 62 | $data[$field] = $_REQUEST[$field]; 63 | } else { 64 | if (!empty($type[1])) { 65 | returnError("$field is required"); 66 | exit; 67 | } 68 | } 69 | } 70 | 71 | 72 | $sql = 'insert into '.$table.' ('; 73 | 74 | foreach ($data as $name => $value) { 75 | $sql .= '`'.$name.'`,'; 76 | } 77 | 78 | $sql = substr($sql,0,-1); 79 | 80 | $sql .= ') values ('; 81 | 82 | foreach ($data as $name => $value) { 83 | $sql .= ':'.$name.','; 84 | } 85 | 86 | $sql = substr($sql,0,-1); 87 | 88 | $sql .= ')'; 89 | 90 | $sth = $dbh->prepare($sql); 91 | $sth->execute($data); 92 | echo $sql; 93 | 94 | } 95 | 96 | function get() { 97 | global $path; 98 | $client = $path[2]; 99 | $site = $path[3]; 100 | $name = $path[4]; 101 | 102 | if ($name == 'ref') { 103 | 104 | $sth = $dbh->prepare("select ref from track where client = :client and site = :site order by id asc limit 1"); 105 | $sth->execute($data); 106 | $ref = $statement->fetch(); 107 | 108 | echo $ref; 109 | 110 | } 111 | 112 | exit; 113 | } 114 | 115 | function returnImage() { 116 | 117 | header("Content-type: image/gif"); 118 | header("Content-Length: 42"); 119 | header("Cache-Control: private, no-cache, no-cache=Set-Cookie, proxy-revalidate"); 120 | header("Expires: Wed, 11 Jan 2000 12:59:00 GMT"); 121 | header("Last-Modified: Wed, 11 Jan 2006 12:59:00 GMT"); 122 | header("Pragma: no-cache"); 123 | 124 | echo sprintf( 125 | '%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%', 126 | 71,73,70,56,57,97,1,0,1,0,128,255,0,192,192,192,0,0,0,33,249,4,1,0,0,0,0,44,0,0,0,0,1,0,1,0,0,2,2,68,1,0,59 127 | ); 128 | 129 | 130 | ob_flush(); 131 | flush(); 132 | ob_end_flush(); 133 | } 134 | 135 | function returnJSON() { 136 | $received = true; 137 | // print(json_encode($received)); 138 | // ob_flush(); 139 | // flush(); 140 | // ob_end_flush(); 141 | } 142 | 143 | function returnError($error) { 144 | echo(json_encode(array("error" => $error))); 145 | 146 | } -------------------------------------------------------------------------------- /controllers/connect.php: -------------------------------------------------------------------------------- 1 | prepare("select * from accounts where companyid = ? and active = 1"); 10 | $query->execute(array($_SESSION['user']['companyid'])); 11 | $accounts = $query->fetchAll(); 12 | 13 | foreach ($accounts as $no => $account) { 14 | 15 | if ($account['type'] == 'facebook') { 16 | $image = 'https://graph.facebook.com/'.$account['data1'].'/picture'; 17 | $accounts[$no]['image'] = $image; 18 | } 19 | 20 | if ($account['type'] == 'twitter') { 21 | $image = $account['data4']; 22 | $accounts[$no]['image'] = $image; 23 | } 24 | 25 | } 26 | 27 | $template->set('accounts',$accounts); 28 | } 29 | 30 | function facebook() { 31 | 32 | global $template; 33 | 34 | $facebook = new Facebook(array('appId' => FB_APPID,'secret' => FB_APPSECRET)); 35 | $user = $facebook->getUser(); 36 | 37 | if ($user) { 38 | try { 39 | 40 | $pages = $facebook->api('/me/accounts'); 41 | $template->set('pages',$pages); 42 | 43 | $permissions = $facebook->api("/me/permissions"); 44 | 45 | $publish_actions = 0; 46 | $manage_pages = 0; 47 | 48 | foreach ($permissions['data'] as $permission) { 49 | if ($permission['permission'] == 'publish_actions') { 50 | $publish_actions = 1; 51 | } 52 | if ($permission['permission'] == 'manage_pages') { 53 | $manage_pages = 1; 54 | } 55 | } 56 | 57 | if ($publish_actions == 0 || $manage_pages == 0) { 58 | throw new Exception("Oops"); 59 | } 60 | 61 | 62 | } catch (Exception $e) { 63 | 64 | $url = $facebook->getLoginUrl(array("scope" => "publish_actions,manage_pages")); 65 | header("Location: ".$url); 66 | exit; 67 | } 68 | 69 | } else { 70 | $url = $facebook->getLoginUrl(array("scope" => "publish_actions,manage_pages")); 71 | header("Location: ".$url); 72 | exit; 73 | } 74 | 75 | } 76 | 77 | function twitter() { 78 | global $template; 79 | 80 | $twitter = new TwitterOAuth(TWITTER_APIKEY, TWITTER_APISECRET); 81 | $request_token = $twitter->getRequestToken(BASE_URL."connect/twitter-callback"); 82 | 83 | $_SESSION['oauth_token'] = $token = $request_token['oauth_token']; 84 | $_SESSION['oauth_token_secret'] = $request_token['oauth_token_secret']; 85 | 86 | switch ($twitter->http_code) { 87 | case 200: 88 | /* Build authorize URL and redirect user to Twitter. */ 89 | $url = $twitter->getAuthorizeURL($token); 90 | header('Location: ' . $url); 91 | exit; 92 | default: 93 | $_SESSION['notification']['type'] = 'error'; 94 | $_SESSION['notification']['message'] = 'Unable to connect to Twitter. Please try again later.'; 95 | header("Location: ".BASE_URL."connect"); 96 | exit; 97 | } 98 | 99 | } 100 | 101 | 102 | function twittercallback() { 103 | 104 | $twitter = new TwitterOAuth(TWITTER_APIKEY, TWITTER_APISECRET, $_SESSION['oauth_token'], $_SESSION['oauth_token_secret']); 105 | $token = $twitter->getAccessToken($_REQUEST['oauth_verifier']); 106 | 107 | $account = $twitter->get('account/verify_credentials'); 108 | 109 | header('Location: '.BASE_URL.'connect/add/twitter/'.base64_encode($token['user_id']).'/'.base64_encode($token['screen_name']).'/'.base64_encode($token['oauth_token']).'/'.base64_encode($token['oauth_token_secret']).'/'.base64_encode($account->profile_image_url_https)); 110 | exit; 111 | 112 | 113 | } 114 | 115 | function add() { 116 | global $dbh; 117 | global $path; 118 | 119 | $type = $path[2]; 120 | 121 | if ($type == 'facebook') { 122 | 123 | $query = $dbh->prepare("select * from accounts where companyid = ? and type = ? and data1 = ?"); 124 | $query->execute(array($_SESSION['user']['companyid'],'facebook',base64_decode($path[3]))); 125 | $existing = $query->fetch(); 126 | 127 | if ($existing['id'] > 0) { 128 | $sql = "update accounts set active = ?, name = ?, data2 = ? where companyid = ? and type = ? and data1 = ?"; 129 | $query = $dbh->prepare($sql); 130 | $query->execute(array(1,base64_decode($path[4]),base64_decode($path[5]),$_SESSION['user']['companyid'],'facebook',base64_decode($path[3]))); 131 | } else { 132 | $sql = "insert into accounts (companyid,type,name,data1,data2,active) VALUES (?,?,?,?,?,?)"; 133 | $query = $dbh->prepare($sql); 134 | $query->execute(array($_SESSION['user']['companyid'],'facebook',base64_decode($path[4]),base64_decode($path[3]),base64_decode($path[5]),1)); 135 | } 136 | } 137 | 138 | if ($type == 'twitter') { 139 | 140 | $query = $dbh->prepare("select * from accounts where companyid = ? and type = ? and data1 = ?"); 141 | $query->execute(array($_SESSION['user']['companyid'],'twitter',base64_decode($path[3]))); 142 | $existing = $query->fetch(); 143 | 144 | if ($existing['id'] > 0) { 145 | $sql = "update accounts set active = ?, name = ?, data2 = ?, data3 = ?, data4 = ? where companyid = ? and type = ? and data1 = ?"; 146 | $query = $dbh->prepare($sql); 147 | $query->execute(array(1,base64_decode($path[4]),base64_decode($path[5]),base64_decode($path[6]),base64_decode($path[7]),$_SESSION['user']['companyid'],'twitter',base64_decode($path[3]))); 148 | } else { 149 | 150 | $sql = "insert into accounts (companyid,type,name,data1,data2,data3,data4,active) VALUES (?,?,?,?,?,?,?,?)"; 151 | $query = $dbh->prepare($sql); 152 | $query->execute(array($_SESSION['user']['companyid'],'twitter',base64_decode($path[4]),base64_decode($path[3]),base64_decode($path[5]),base64_decode($path[6]),base64_decode($path[7]),1)); 153 | 154 | } 155 | 156 | } 157 | 158 | $_SESSION['notification']['type'] = 'success'; 159 | $_SESSION['notification']['message'] = 'Account has been successfully added.'; 160 | 161 | header("Location: ".BASE_URL."connect"); 162 | exit; 163 | 164 | } 165 | 166 | function remove() { 167 | global $dbh; 168 | global $path; 169 | 170 | $accountId = intval($path[2]); 171 | 172 | $query = $dbh->prepare("update accounts set active = ? where companyid = ? and id = ?"); 173 | $query->execute(array(0,$_SESSION['user']['companyid'],$accountId)); 174 | 175 | $_SESSION['notification']['type'] = 'success'; 176 | $_SESSION['notification']['message'] = 'Account has been successfully removed.'; 177 | 178 | header("Location: ".BASE_URL."connect"); 179 | exit; 180 | } -------------------------------------------------------------------------------- /controllers/cron.php: -------------------------------------------------------------------------------- 1 | prepare("delete from suggestions"); 8 | $query->execute(); 9 | 10 | 11 | $twitter = new TwitterOAuth(TWITTER_APIKEY, TWITTER_APISECRET, TWITTER_TOKEN, TWITTER_SECRET); 12 | 13 | foreach ($suggestionLists as $list) { 14 | 15 | $tweets = $twitter->get('lists/statuses',array('slug'=>$list,'owner_screen_name'=>TWITTER_USERNAME,'count'=>'10')); 16 | 17 | foreach ($tweets as $tweet) { 18 | $id = $tweet->id_str; 19 | $text = $tweet->text; 20 | $via = $tweet->user->screen_name; 21 | 22 | if (!empty($tweet->entities->media[0]->media_url_https)) { 23 | $media = $tweet->entities->media[0]->media_url_https; 24 | $text = preg_replace('/(.*)\shttp:\/\/t\.co(.*?)$/', '$1', $text); // Remove last link 25 | } else { 26 | $media = ''; 27 | } 28 | 29 | $r = array(); 30 | $r[0] = $tweet->in_reply_to_status_id; 31 | $r[1] = $tweet->in_reply_to_status_id_str; 32 | $r[2] = $tweet->in_reply_to_user_id; 33 | $r[3] = $tweet->in_reply_to_user_id_str; 34 | $r[4] = $tweet->in_reply_to_screen_name; 35 | 36 | if (empty($r[0]) && empty($r[1]) && empty($r[2]) && empty($r[3]) && empty($r[4])) { 37 | $query = $dbh->prepare("insert ignore into suggestions (id,text,screen_name,media,list,record_created) values (?,?,?,?,?,?)"); 38 | $query->execute(array($id,$text,$via,$media,$list,time())); 39 | 40 | } 41 | } 42 | 43 | } 44 | 45 | exit; 46 | } 47 | 48 | function post() { 49 | global $dbh; 50 | 51 | $query = $dbh->prepare("select accounts_queue.*, accounts.type, accounts.data1, accounts.data2, accounts.data3 from accounts_queue join accounts on accounts_queue.accountid = accounts.id where scheduled_time <= ? and (sent_time is null or sent_time = '') and accounts.active = 1 limit 10"); 52 | $query->execute(array(time())); 53 | $posts = $query->fetchAll(); 54 | 55 | foreach ($posts as $post) { 56 | 57 | $error = ''; 58 | 59 | if ($post['type'] == 'facebook') { 60 | 61 | try { 62 | 63 | $params = array( 64 | "access_token" => $post['data2'], 65 | "message" => $post['message'] 66 | ); 67 | 68 | $facebook = new Facebook(array('appId' => FB_APPID,'secret' => FB_APPSECRET)); 69 | 70 | if (!empty($post['image'])) { 71 | $params['url'] = $post['image']; 72 | $ret = $facebook->api('/'.$post['data1'].'/photos', 'POST', $params); 73 | } else { 74 | 75 | unset($matches); 76 | 77 | preg_match_all('!(http|ftp|scp)(s)?:\/\/[a-zA-Z0-9.?&_/]+!',$post['message'],$matches); 78 | 79 | $link = ''; 80 | 81 | if (!empty($matches[0][0])) { 82 | $link = $matches[0][0]; 83 | $params['link'] = $link; 84 | } 85 | 86 | $ret = $facebook->api('/'.$post['data1'].'/feed', 'POST', $params); 87 | } 88 | 89 | } catch(Exception $e) { 90 | $error = print_r($e,true); 91 | } 92 | 93 | } 94 | 95 | if ($post['type'] == 'twitter') { 96 | if (empty($post['image'])) { 97 | $twitter = new TwitterOAuth(TWITTER_APIKEY, TWITTER_APISECRET, $post['data2'], $post['data3']); 98 | $account = $twitter->get('account/verify_credentials'); 99 | $status = $twitter->post('statuses/update', array('status' => $post['message'])); 100 | 101 | if (!empty($status->errors[0]->message)) { 102 | $error = $status->errors[0]->message; 103 | } 104 | 105 | } else { 106 | $file = $post['image']; 107 | 108 | $params = array( 109 | 'media[]' => file_get_contents($file) 110 | ); 111 | 112 | if (null !== $post['message']) { 113 | $params['status'] = $post['message']; 114 | } 115 | 116 | $tmhOAuth = new \tmhOAuth(array( 117 | 'user_agent' => 'SocialTurn', 118 | 'consumer_key' => TWITTER_APIKEY, 119 | 'consumer_secret' => TWITTER_APISECRET, 120 | 'token' => $post['data2'], 121 | 'secret' => $post['data3'], 122 | )); 123 | 124 | $response = $tmhOAuth->user_request(array( 125 | 'method' => 'POST', 126 | 'url' => $tmhOAuth->url("1.1/statuses/update_with_media"), 127 | 'params' => $params, 128 | 'multipart' => true 129 | )); 130 | 131 | if ($response == 200 && isset($tmhOAuth->response['response'])){ 132 | $json = json_decode($tmhOAuth->response['response']); 133 | $lastTweetId = $json->id_str; 134 | } 135 | 136 | if (isset($tmhOAuth->response['response'])) { 137 | $json = json_decode($tmhOAuth->response['response']); 138 | if (isset($json->errors)) { 139 | $error = array(); 140 | foreach ($json->errors as $err) { 141 | $error[] = '['.$err->code.'] '.$err->message; 142 | } 143 | 144 | $error = $err->message; 145 | } 146 | } 147 | 148 | } 149 | } 150 | 151 | 152 | $update = $dbh->prepare("update accounts_queue set sent_time = ?, error = ? where id = ?"); 153 | $update->execute(array(time(),$error,$post['id'])); 154 | 155 | 156 | } 157 | 158 | exit; 159 | } 160 | -------------------------------------------------------------------------------- /controllers/helpers.php: -------------------------------------------------------------------------------- 1 | set('user',$user); 11 | return $helper->render(); 12 | } -------------------------------------------------------------------------------- /controllers/home.php: -------------------------------------------------------------------------------- 1 | set('noextra','1'); 6 | } 7 | 8 | function permissions() { 9 | global $template; 10 | $template->set('noextra','1'); 11 | } 12 | 13 | function noaccounts() { 14 | global $template; 15 | $template->set('noextra','1'); 16 | } -------------------------------------------------------------------------------- /controllers/social.php: -------------------------------------------------------------------------------- 1 | prepare("select accounts.* from accounts join users_accounts on accounts.id = users_accounts.accountid where users_accounts.companyid = ? and users_accounts.userid = ? and accounts.active = 1"); 21 | $query->execute(array($_SESSION['user']['companyid'],$_SESSION['user']['loggedin'])); 22 | $accounts = $query->fetchAll(); 23 | } else { 24 | $query = $dbh->prepare("select * from accounts where companyid = ? and accounts.active = 1"); 25 | $query->execute(array($_SESSION['user']['companyid'])); 26 | $accounts = $query->fetchAll(); 27 | } 28 | 29 | $currentpermission = 0; 30 | 31 | foreach ($accounts as $no => $account) { 32 | 33 | // If no account selected, redirect to first account 34 | if (empty($current)) { 35 | header("Location: ".BASE_URL."social/queue/".$account['id']); 36 | exit; 37 | } 38 | 39 | if ($account['type'] == 'facebook') { 40 | $image = 'https://graph.facebook.com/'.$account['data1'].'/picture'; 41 | $accounts[$no]['image'] = $image; 42 | } 43 | 44 | if ($account['type'] == 'twitter') { 45 | $image = $account['data4']; 46 | $accounts[$no]['image'] = $image; 47 | } 48 | 49 | if ($current == $account['id']) { 50 | $accounts[$no]['current'] = 1; 51 | $currentpermission = 1; 52 | } 53 | } 54 | 55 | // If no account found, give a friendly error message 56 | if (empty($current)) { 57 | if ($_SESSION['user']['type'] == 1) { 58 | header("Location: ".BASE_URL."connect"); 59 | exit; 60 | } else { 61 | header("Location: ".BASE_URL."oops/no-accounts"); 62 | exit; 63 | } 64 | } 65 | 66 | // If no permission 67 | if (empty($currentpermission)) { 68 | header("Location: ".BASE_URL."oops/permissions"); 69 | exit; 70 | } 71 | 72 | $template->set('current',$current); 73 | $template->set('accounts',$accounts); 74 | 75 | } 76 | 77 | function requeue($accountId) { 78 | 79 | global $dbh; 80 | 81 | $query = $dbh->prepare("select timezone from accounts_schedule where accountid = ? order by record_created desc limit 1"); 82 | $query->execute(array($accountId)); 83 | $timezone = $query->fetch(); 84 | 85 | if (!empty($timezone['timezone'])) { 86 | $timezone = $timezone['timezone']; 87 | } else { 88 | $timezone = 0; 89 | } 90 | 91 | $query = $dbh->prepare("select * from accounts_schedule where accountid = ?"); 92 | $query->execute(array($accountId)); 93 | $schedule = $query->fetchAll(); 94 | 95 | if (!empty($schedule)) { 96 | 97 | $days = array('','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'); 98 | 99 | $postTiming = array(); 100 | 101 | foreach ($schedule as $s) { 102 | $day = strtotime($days[$s['day']].' this week'); 103 | $day += ($s['time']*36); 104 | 105 | $day -= ($timezone*3600); 106 | 107 | if ($day >= time()) { 108 | $postTiming[] = $day; 109 | } 110 | } 111 | 112 | $postTimingWeek2 = array(); 113 | 114 | foreach ($schedule as $s) { 115 | $day = strtotime($days[$s['day']].' next week'); 116 | $day += ($s['time']*36); 117 | 118 | $day -= ($timezone*3600); 119 | 120 | if ($day >= time()) { 121 | $postTimingWeek2[] = $day; 122 | } 123 | } 124 | 125 | $timing = array(); 126 | $week = 0; 127 | 128 | for ($i = 0;$i<100;$i++) { 129 | if (!empty($postTiming)) { 130 | $timing[] = array_shift($postTiming); 131 | } else { 132 | $timing[] = current($postTimingWeek2)+$week; 133 | next($postTimingWeek2); 134 | if (current($postTimingWeek2) == FALSE) { 135 | reset($postTimingWeek2); 136 | $week += 604800; 137 | } 138 | } 139 | } 140 | 141 | $query = $dbh->prepare("select accounts_queue.id from accounts_queue where accounts_queue.accountid = ? and scheduled_time <> 1 and (sent_time is null or sent_time = '') order by id asc limit 100"); 142 | $query->execute(array($accountId)); 143 | $queue = $query->fetchAll(); 144 | 145 | if (!empty($queue)) { 146 | 147 | $query = "update accounts_queue set scheduled_time = case "; 148 | 149 | foreach ($queue as $post) { 150 | $query .= "when id = ".$post['id']." then ".array_shift($timing)." "; 151 | $ids[] = $post['id']; 152 | } 153 | 154 | $query .= "end where id IN (".implode(",",$ids).")"; 155 | $query = $dbh->prepare($query); 156 | $query->execute(); 157 | 158 | } 159 | 160 | } 161 | 162 | } 163 | 164 | function queue() { 165 | global $dbh; 166 | global $template; 167 | global $path; 168 | 169 | $accountId = intval($path[2]); 170 | 171 | $query = $dbh->prepare("select timezone from accounts_schedule where companyid = ? and accountid = ? order by record_created desc limit 1"); 172 | $query->execute(array($_SESSION['user']['companyid'],$accountId)); 173 | $timezone = $query->fetch(); 174 | 175 | if (!empty($timezone['timezone'])) { 176 | $timezone = $timezone['timezone']; 177 | } else { 178 | $timezone = 0; 179 | } 180 | 181 | $query = $dbh->prepare("select accounts_queue.*, users.email from accounts_queue join users on accounts_queue.userid = users.id where accounts_queue.companyid = ? and accounts_queue.accountid = ? and ((scheduled_time <> 1 and (sent_time is null or sent_time = '')) or (error <> '')) order by id asc"); 182 | $query->execute(array($_SESSION['user']['companyid'],$accountId)); 183 | $queue = $query->fetchAll(); 184 | 185 | $template->set('timezone',$timezone); 186 | $template->set('posts',$queue); 187 | } 188 | 189 | function queueremove() { 190 | global $dbh; 191 | global $template; 192 | global $path; 193 | 194 | $accountId = intval($path[2]); 195 | 196 | if (!empty($path[3])) { 197 | $postId = intval($path[3]); 198 | } else { 199 | $_SESSION['notification']['type'] = 'error'; 200 | $_SESSION['notification']['message'] = 'Oops! Something went wrong.'; 201 | header("Location: ".$_SERVER['HTTP_REFERER']); 202 | exit; 203 | } 204 | 205 | $query = $dbh->prepare("delete from accounts_queue where companyid = ? and accountid = ? and id = ? limit 1"); 206 | $query->execute(array($_SESSION['user']['companyid'],$accountId, $postId)); 207 | requeue($accountId); 208 | 209 | $_SESSION['notification']['type'] = 'success'; 210 | $_SESSION['notification']['message'] = 'We have removed the post from the queue.'; 211 | header("Location: ".$_SERVER['HTTP_REFERER']); 212 | exit; 213 | 214 | 215 | } 216 | 217 | function queueresend() { 218 | global $dbh; 219 | global $template; 220 | global $path; 221 | 222 | $accountId = intval($path[2]); 223 | 224 | if (!empty($path[3])) { 225 | $postId = intval($path[3]); 226 | } else { 227 | $_SESSION['notification']['type'] = 'error'; 228 | $_SESSION['notification']['message'] = 'Oops! Something went wrong.'; 229 | header("Location: ".$_SERVER['HTTP_REFERER']); 230 | exit; 231 | } 232 | 233 | $query = $dbh->prepare("update accounts_queue set error = '', scheduled_time = 0, sent_time = 0 where companyid = ? and accountid = ? and id = ? limit 1"); 234 | $query->execute(array($_SESSION['user']['companyid'],$accountId, $postId)); 235 | requeue($accountId); 236 | 237 | $_SESSION['notification']['type'] = 'success'; 238 | $_SESSION['notification']['message'] = 'We have rescheduled the post. If you receive the error again, be sure to reconnect your account.'; 239 | header("Location: ".$_SERVER['HTTP_REFERER']); 240 | exit; 241 | 242 | 243 | } 244 | 245 | 246 | function queuenow() { 247 | global $dbh; 248 | global $template; 249 | global $path; 250 | 251 | $accountId = intval($path[2]); 252 | 253 | if (!empty($path[3])) { 254 | $postId = intval($path[3]); 255 | } else { 256 | $_SESSION['notification']['type'] = 'error'; 257 | $_SESSION['notification']['message'] = 'Oops! Something went wrong.'; 258 | header("Location: ".$_SERVER['HTTP_REFERER']); 259 | exit; 260 | } 261 | 262 | $query = $dbh->prepare("update accounts_queue set scheduled_time = 1 where companyid = ? and accountid = ? and id = ? limit 1"); 263 | $query->execute(array($_SESSION['user']['companyid'],$accountId, $postId)); 264 | requeue($accountId); 265 | 266 | $_SESSION['notification']['type'] = 'success'; 267 | $_SESSION['notification']['message'] = 'Your post is on its way!'; 268 | header("Location: ".$_SERVER['HTTP_REFERER']); 269 | exit; 270 | 271 | 272 | } 273 | 274 | function manual(){ 275 | 276 | } 277 | 278 | function suggestions() { 279 | global $dbh; 280 | global $template; 281 | global $path; 282 | global $suggestionLists; 283 | 284 | $accountId = intval($path[2]); 285 | 286 | $currentSuggestion = 'all'; 287 | 288 | if (!empty($path[3]) && in_array($path[3],$suggestionLists)) { 289 | $currentSuggestion = $path[3]; 290 | $query = $dbh->prepare("select * from suggestions where id not in (select suggestion_id from accounts_queue where accountid = ?) and list = ? order by id desc limit 25"); 291 | $query->execute(array($accountId,$currentSuggestion)); 292 | } else { 293 | $query = $dbh->prepare("select * from suggestions where id not in (select suggestion_id from accounts_queue where accountid = ?) order by id desc limit 25"); 294 | $query->execute(array($accountId)); 295 | } 296 | 297 | $suggestions = $query->fetchAll(); 298 | 299 | $template->set('suggestions',$suggestions); 300 | $template->set('list',$suggestionLists); 301 | $template->set('currentSuggestion',$currentSuggestion); 302 | 303 | } 304 | 305 | function schedule() { 306 | global $dbh; 307 | global $template; 308 | global $path; 309 | 310 | $accountId = intval($path[2]); 311 | 312 | $query = $dbh->prepare("select timezone from accounts_schedule where companyid = ? and accountid = ? order by record_created desc limit 1"); 313 | $query->execute(array($_SESSION['user']['companyid'],$accountId)); 314 | $timezone = $query->fetch(); 315 | 316 | if (!empty($timezone['timezone'])) { 317 | $timezone = $timezone['timezone']; 318 | } else { 319 | $timezone = 0; 320 | } 321 | 322 | $query = $dbh->prepare("select distinct day from accounts_schedule where companyid = ? and accountid = ?"); 323 | $query->execute(array($_SESSION['user']['companyid'],$accountId)); 324 | $d = $query->fetchAll(); 325 | 326 | $days = array(); 327 | 328 | foreach ($d as $day) { 329 | $days[$day['day']] = 1; 330 | } 331 | 332 | $query = $dbh->prepare("select distinct time from accounts_schedule where companyid = ? and accountid = ?"); 333 | $query->execute(array($_SESSION['user']['companyid'],$accountId)); 334 | $t = $query->fetchAll(); 335 | 336 | $times = array(); 337 | 338 | foreach ($t as $time) { 339 | if (strlen($time['time']) == 3) { 340 | $time['time'] = '0'.$time['time']; 341 | } 342 | 343 | $times[$time['time']] = 1; 344 | } 345 | 346 | 347 | $template->set('timezone',$timezone); 348 | $template->set('days',$days); 349 | $template->set('times',$times); 350 | 351 | } 352 | 353 | function scheduleupdate() { 354 | 355 | global $dbh; 356 | global $template; 357 | global $path; 358 | 359 | // $current is already verified in pre() 360 | $accountId = intval($path[2]); 361 | 362 | $query = $dbh->prepare("delete from accounts_schedule where companyid = ? and accountid = ?"); 363 | $query->execute(array($_SESSION['user']['companyid'],$accountId)); 364 | 365 | if (!empty($_POST['days'])) { 366 | foreach ($_POST['days'] as $day) { 367 | if (!empty($_POST['time'])) { 368 | foreach ($_POST['time'] as $time) { 369 | 370 | $query = $dbh->prepare("insert into accounts_schedule (companyid,accountid,userid,timezone,day,time,record_created) values (?,?,?,?,?,?,?)"); 371 | $query->execute(array($_SESSION['user']['companyid'],$accountId,$_SESSION['user']['loggedin'],$_POST['timezone'],$day,$time,time())); 372 | 373 | } 374 | } 375 | } 376 | } 377 | 378 | requeue($accountId); 379 | 380 | $_SESSION['notification']['type'] = 'success'; 381 | $_SESSION['notification']['message'] = 'Yay! All future posts will be scheduled accordingly.'; 382 | header("Location: ".BASE_URL."social/schedule/".$accountId); 383 | exit; 384 | 385 | } 386 | 387 | function sent() { 388 | 389 | } 390 | 391 | function post() { 392 | 393 | global $dbh; 394 | global $path; 395 | // $current is already verified in pre() 396 | $accountId = intval($path[2]); 397 | 398 | 399 | if (empty($_POST['message'])) { 400 | $_SESSION['notification']['type'] = 'error'; 401 | $_SESSION['notification']['message'] = 'Oops! You forgot to post an update. Be careful.'; 402 | header("Location: ".$_SERVER['HTTP_REFERER']); 403 | exit; 404 | } 405 | 406 | $image = ''; 407 | 408 | if (!empty($_FILES['image']['tmp_name'])) { 409 | 410 | if (isset($_FILES['image']['tmp_name'])) { 411 | $file = $_FILES['image']['tmp_name']; 412 | $storagePath = dirname(dirname(__FILE__)).'/images'; 413 | $allowedMimes = array('image/png', 'image/jpg', 'image/jpeg', 'image/gif', 'image/pjpeg'); 414 | 415 | $fileName = upload($file, $storagePath, $allowedMimes); 416 | if (!$fileName) { 417 | $_SESSION['notification']['type'] = 'error'; 418 | $_SESSION['notification']['message'] = 'Oops! Your image is not valid. Please double check.'; 419 | header("Location: ".$_SERVER['HTTP_REFERER']); 420 | exit; 421 | } else { 422 | $image = BASE_URL.'images/'.$fileName; 423 | } 424 | } 425 | 426 | } 427 | 428 | 429 | if ($_SESSION['user']['type'] != 1) { 430 | $query = $dbh->prepare("select accounts.* from accounts join users_accounts on accounts.id = users_accounts.accountid where users_accounts.companyid = ? and users_accounts.userid = ?"); 431 | $query->execute(array($_SESSION['user']['companyid'],$_SESSION['user']['loggedin'])); 432 | $access = $query->fetchAll(); 433 | } else { 434 | $query = $dbh->prepare("select * from accounts where companyid = ?"); 435 | $query->execute(array($_SESSION['user']['companyid'])); 436 | $access = $query->fetchAll(); 437 | } 438 | 439 | foreach ($access as $account) { 440 | $present[] = $account['id']; 441 | $accountInfo[$account['id']] = $account; 442 | } 443 | 444 | if (empty($_POST['accounts'])) { 445 | $_POST['accounts'] = array($accountId); 446 | } 447 | 448 | if (!empty($_POST['media'])) { 449 | $image = $_POST['media']; 450 | } 451 | 452 | 453 | if (!empty($_POST['accounts'])) { 454 | foreach ($_POST['accounts'] as $account) { 455 | if (in_array($account,$present)) { 456 | 457 | $_SESSION['notification']['type'] = 'success'; 458 | 459 | if ($_POST['when'] == 'now') { 460 | $_POST['when'] = 1; 461 | $_SESSION['notification']['message'] = 'Yay! Your update will be posted within a few minutes!'; 462 | } else if ($_POST['when'] == 'queue') { 463 | $_POST['when'] = 0; 464 | $_SESSION['notification']['message'] = 'Yay! Your update is queued and will be posted as per your schedule.'; 465 | } 466 | 467 | if (!empty($_POST['screen_name'])) { 468 | if (strlen($_POST['message'].' via @'.$_POST['screen_name']) <= 140) { 469 | $_POST['message'] = $_POST['message'].' via @'.$_POST['screen_name']; 470 | } 471 | } 472 | 473 | $sid = 0; 474 | 475 | if (!empty($_POST['suggestion_id'])) { 476 | $sid = $_POST['suggestion_id']; 477 | } 478 | 479 | $query = $dbh->prepare("insert into accounts_queue (companyid,accountid,userid,message,image,record_created,scheduled_time,suggestion_id) values (?,?,?,?,?,?,?,?)"); 480 | $query->execute(array($_SESSION['user']['companyid'],$accountInfo[$account]['id'],$_SESSION['user']['loggedin'],$_POST['message'],$image,time(),$_POST['when'],$sid)); 481 | requeue($accountInfo[$account]['id']); 482 | 483 | } 484 | } 485 | } 486 | 487 | header("Location: ".$_SERVER['HTTP_REFERER']); 488 | exit; 489 | } 490 | -------------------------------------------------------------------------------- /controllers/team.php: -------------------------------------------------------------------------------- 1 | prepare("select * from users where active = 1 and companyid = ?"); 10 | $query->execute(array($_SESSION['user']['companyid'])); 11 | $users = $query->fetchAll(); 12 | 13 | $template->set('users',$users); 14 | } 15 | 16 | function invite() { 17 | 18 | } 19 | 20 | function invited() { 21 | global $template; 22 | $email = $_POST['email']; 23 | $template->set('email',$email); 24 | } 25 | 26 | function manage(){ 27 | global $template; 28 | global $path; 29 | global $dbh; 30 | 31 | $userId = intval($path[2]); 32 | 33 | $query = $dbh->prepare("select * from users where id = ? and active = 1 and companyid = ? and type <> 1"); 34 | $query->execute(array($userId,$_SESSION['user']['companyid'])); 35 | $user = $query->fetch(); 36 | 37 | if (empty($user['id'])) { 38 | error404(); 39 | } 40 | 41 | $query = $dbh->prepare("select * from accounts where companyid = ?"); 42 | $query->execute(array($_SESSION['user']['companyid'])); 43 | $accounts = $query->fetchAll(); 44 | 45 | $template->set('accounts',$accounts); 46 | 47 | 48 | $query = $dbh->prepare("select accounts.* from accounts join users_accounts on accounts.id = users_accounts.accountid where users_accounts.companyid = ? and users_accounts.userid = ?"); 49 | $query->execute(array($_SESSION['user']['companyid'],$userId)); 50 | $access = $query->fetchAll(); 51 | 52 | $present = array(); 53 | 54 | foreach ($access as $account) { 55 | $present[] = $account['id']; 56 | } 57 | 58 | $template->set('present',$present); 59 | 60 | $template->set('user',$user); 61 | } 62 | 63 | function update() { 64 | global $template; 65 | global $path; 66 | global $dbh; 67 | 68 | // Are we updating the correct user? 69 | 70 | $userId = $_POST['id']; 71 | 72 | $query = $dbh->prepare("select * from users where id = ? and active = 1 and companyid = ? and type <> 1"); 73 | $query->execute(array($userId,$_SESSION['user']['companyid'])); 74 | $user = $query->fetch(); 75 | 76 | if (empty($user['id'])) { 77 | error404(); 78 | } 79 | 80 | if (!empty($_POST['role']) && $_POST['role'] > 1) { 81 | $sql = "update users set type = ? where id = ? and companyid = ?"; 82 | $query = $dbh->prepare($sql); 83 | $query->execute(array($_POST['role'],$_POST['id'],$_SESSION['user']['companyid'])); 84 | } 85 | 86 | $query = $dbh->prepare("delete from users_accounts where companyid = ? and userid = ?"); 87 | $query->execute(array($_SESSION['user']['companyid'],$_POST['id'])); 88 | 89 | if (!empty($_POST['accounts'])) { 90 | foreach ($_POST['accounts'] as $account) { 91 | $query = $dbh->prepare("insert into users_accounts (companyid,userid,accountid) values (?,?,?)"); 92 | $query->execute(array($_SESSION['user']['companyid'],$_POST['id'],$account)); 93 | } 94 | } 95 | 96 | if ($_POST['type'] == 'delete') { 97 | $sql = "update users set active = 0 where id = ? and companyid = ?"; 98 | $query = $dbh->prepare($sql); 99 | $query->execute(array($_POST['id'],$_SESSION['user']['companyid'])); 100 | 101 | $_SESSION['notification']['type'] = 'success'; 102 | $_SESSION['notification']['message'] = $user['email'].' has been successfully removed.'; 103 | } else { 104 | $_SESSION['notification']['type'] = 'success'; 105 | $_SESSION['notification']['message'] = 'Yay! Permissions have been successfully modified for '.$user['email']; 106 | } 107 | 108 | header("Location: ".BASE_URL."team"); 109 | exit; 110 | } -------------------------------------------------------------------------------- /controllers/users.php: -------------------------------------------------------------------------------- 1 | set('noextra','1'); 7 | $template->set('heading','login'); 8 | $template->set('title','Login'); 9 | } 10 | 11 | function validate() { 12 | global $dbh; 13 | 14 | $type = $_POST['type']; 15 | $email = $_POST['email']; 16 | $password = $_POST['password']; 17 | $code = ''; 18 | 19 | if (!empty($_POST['code'])) { 20 | $code = $_POST['code']; 21 | } 22 | 23 | if (empty($_POST['email']) || empty($_POST['password'])) { 24 | $_SESSION['notification']['type'] = 'error'; 25 | $_SESSION['notification']['message'] = 'Oops! Looks like you missed some details.'; 26 | header("Location: ".$_SERVER['HTTP_REFERER']); 27 | exit; 28 | } 29 | 30 | if ($type == 'login') { 31 | 32 | $query = $dbh->prepare("select * from users where email = ? and password = ? and active = 1"); 33 | $query->execute(array($email,hashPassword($password))); 34 | $account = $query->fetch(); 35 | 36 | if (!empty($account['id'])) { 37 | $_SESSION['user']['loggedin'] = $account['id']; 38 | $_SESSION['user']['email'] = $account['email']; 39 | $_SESSION['user']['companyid'] = $account['companyid']; 40 | $_SESSION['user']['type'] = $account['type']; 41 | 42 | header("Location: ".BASE_URL); 43 | exit; 44 | } else { 45 | $_SESSION['notification']['type'] = 'error'; 46 | $_SESSION['notification']['message'] = 'Oops! Looks like your login information is incorrect.'; 47 | header("Location: ".$_SERVER['HTTP_REFERER']); 48 | exit; 49 | } 50 | 51 | 52 | } else if ($type == 'register') { 53 | 54 | if (!empty($_POST['name'])) { 55 | $name = $_POST['name']; 56 | } else { 57 | $_SESSION['notification']['type'] = 'error'; 58 | $_SESSION['notification']['message'] = 'Looks like you forgot to enter your team\'s name.'; 59 | header("Location: ".$_SERVER['HTTP_REFERER']); 60 | exit; 61 | } 62 | 63 | $query = $dbh->prepare("select * from users where email = ?"); 64 | $query->execute(array($email)); 65 | $account = $query->fetch(); 66 | 67 | if (!empty($account['id'])) { 68 | $_SESSION['notification']['type'] = 'error'; 69 | $_SESSION['notification']['message'] = 'Looks like you already have an account. Please use our forgot password facility.'; 70 | header("Location: ".$_SERVER['HTTP_REFERER']); 71 | exit; 72 | } 73 | 74 | $sql = "INSERT INTO companies (name,plan,active) VALUES (?,?,?)"; 75 | $query = $dbh->prepare($sql); 76 | $query->execute(array($name,1,1)); 77 | $companyId = $dbh->lastInsertId(); 78 | 79 | $sql = "INSERT INTO users (companyid,email,password,active,type) VALUES (?,?,?,?,?)"; 80 | $query = $dbh->prepare($sql); 81 | $query->execute(array($companyId,$email,hashPassword($password),1,1)); 82 | 83 | $_SESSION['user']['loggedin'] = $dbh->lastInsertId(); 84 | $_SESSION['user']['email'] = $email; 85 | $_SESSION['user']['companyid'] = $companyId; 86 | $_SESSION['user']['type'] = 1; 87 | 88 | 89 | header("Location: ".BASE_URL); 90 | exit; 91 | 92 | } else if ($type == 'invite') { 93 | 94 | $query = $dbh->prepare("select * from users where email = ?"); 95 | $query->execute(array($email)); 96 | $account = $query->fetch(); 97 | 98 | if (!empty($account['id'])) { 99 | $_SESSION['notification']['type'] = 'error'; 100 | $_SESSION['notification']['message'] = 'Looks like you already have an account. We currently support only 1 team per email, sorry!'; 101 | header('Location: '.$_SERVER['HTTP_REFERER']); 102 | exit; 103 | } 104 | 105 | $query = $dbh->prepare("select * from companies where sha1(concat(id,?)) = ?"); 106 | $query->execute(array($email,$code)); 107 | $company = $query->fetch(); 108 | 109 | if (empty($company['id'])) { 110 | $_SESSION['notification']['type'] = 'error'; 111 | $_SESSION['notification']['message'] = 'Looks like something has gone wrong. Please ask your team member to invite you again.'; 112 | header('Location: '.$_SERVER['HTTP_REFERER']); 113 | exit; 114 | } 115 | 116 | $companyId = $company['id']; 117 | 118 | $sql = "INSERT INTO users (companyid,email,password,active,type) VALUES (?,?,?,?,?)"; 119 | $query = $dbh->prepare($sql); 120 | $query->execute(array($companyId,$email,hashPassword($password),1,100)); 121 | 122 | $_SESSION['user']['loggedin'] = $dbh->lastInsertId(); 123 | $_SESSION['user']['email'] = $email; 124 | $_SESSION['user']['companyid'] = $companyId; 125 | $_SESSION['user']['type'] = 100; 126 | 127 | 128 | header("Location: ".BASE_URL); 129 | exit; 130 | 131 | } 132 | 133 | } 134 | 135 | function register() { 136 | global $template; 137 | 138 | $template->set('noextra','1'); 139 | 140 | } 141 | 142 | function logout() { 143 | session_destroy(); 144 | header("Location: ".BASE_URL); 145 | exit; 146 | } 147 | 148 | function invite() { 149 | global $template; 150 | global $path; 151 | 152 | $email = base64_decode($path[2]); 153 | $code = $path[3]; 154 | 155 | if (empty($code) || empty($email)) { 156 | error404(); 157 | } 158 | 159 | $template->set('code',$code); 160 | $template->set('email',$email); 161 | $template->set('noextra','1'); 162 | $template->set('title',"You've been invited!"); 163 | } 164 | 165 | function inform() { 166 | global $template; 167 | global $path; 168 | 169 | // EMAIL 170 | 171 | $template->set('noextra','1'); 172 | 173 | } -------------------------------------------------------------------------------- /db/1.txt: -------------------------------------------------------------------------------- 1 | CREATE TABLE `users_accounts` ( 2 | `id` int(11) NOT NULL AUTO_INCREMENT, 3 | `companyid` int(11) DEFAULT NULL, 4 | `accountid` int(11) DEFAULT NULL, 5 | `userid` int(11) DEFAULT NULL, 6 | PRIMARY KEY (`id`) 7 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 8 | 9 | CREATE TABLE `users` ( 10 | `id` int(11) NOT NULL AUTO_INCREMENT, 11 | `companyid` int(11) DEFAULT NULL, 12 | `email` varchar(255) DEFAULT NULL, 13 | `password` varchar(255) DEFAULT NULL, 14 | `active` int(1) DEFAULT '0', 15 | `type` int(1) DEFAULT '0', 16 | `record_created` int(11) DEFAULT '0', 17 | PRIMARY KEY (`id`) 18 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 19 | 20 | CREATE TABLE `suggestions` ( 21 | `id` varchar(45) NOT NULL, 22 | `text` text, 23 | `screen_name` varchar(255) DEFAULT NULL, 24 | `media` text, 25 | `list` varchar(45) DEFAULT NULL, 26 | `record_created` int(11) DEFAULT NULL, 27 | PRIMARY KEY (`id`) 28 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 29 | 30 | CREATE TABLE `companies` ( 31 | `id` int(11) NOT NULL AUTO_INCREMENT, 32 | `email` varchar(255) DEFAULT NULL, 33 | `plan` int(11) DEFAULT NULL, 34 | `active` int(1) DEFAULT NULL, 35 | PRIMARY KEY (`id`) 36 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 37 | 38 | CREATE TABLE `accounts_schedule` ( 39 | `id` int(11) NOT NULL AUTO_INCREMENT, 40 | `companyid` int(11) DEFAULT NULL, 41 | `accountid` int(11) DEFAULT NULL, 42 | `userid` int(11) DEFAULT NULL, 43 | `timezone` varchar(45) DEFAULT NULL, 44 | `day` int(11) DEFAULT NULL, 45 | `time` int(11) DEFAULT NULL, 46 | `record_created` int(11) DEFAULT NULL, 47 | PRIMARY KEY (`id`) 48 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 49 | 50 | CREATE TABLE `accounts_queue` ( 51 | `id` int(11) NOT NULL AUTO_INCREMENT, 52 | `companyid` int(11) DEFAULT NULL, 53 | `accountid` int(11) DEFAULT NULL, 54 | `userid` int(11) DEFAULT NULL, 55 | `message` text, 56 | `image` text, 57 | `suggestion_id` varchar(50) DEFAULT '0', 58 | `scheduled_time` int(11) DEFAULT '0', 59 | `sent_time` int(11) DEFAULT '0', 60 | `error` text, 61 | `record_created` int(11) DEFAULT '0', 62 | PRIMARY KEY (`id`) 63 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 64 | 65 | CREATE TABLE `accounts` ( 66 | `id` int(11) NOT NULL AUTO_INCREMENT, 67 | `companyid` int(11) DEFAULT NULL, 68 | `type` enum('google','facebook','twitter') DEFAULT NULL, 69 | `name` varchar(255) DEFAULT NULL, 70 | `data1` text, 71 | `data2` text, 72 | `data3` text, 73 | `data4` text, 74 | `active` int(11) DEFAULT '0', 75 | `record_created` int(11) DEFAULT '0', 76 | PRIMARY KEY (`id`) 77 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 78 | 79 | 80 | -------------------------------------------------------------------------------- /db/2.txt: -------------------------------------------------------------------------------- 1 | ALTER TABLE `companies` CHANGE COLUMN `email` `name` VARCHAR(255) NULL DEFAULT NULL ; 2 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anantgarg/socialturn/a48c16cfcca89df4e5398698af39b14bc9b7de75/favicon.ico -------------------------------------------------------------------------------- /images/index.htm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anantgarg/socialturn/a48c16cfcca89df4e5398698af39b14bc9b7de75/images/index.htm -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | render(); 97 | 98 | } else { 99 | /* 404 error here */ 100 | error404(); 101 | } 102 | } -------------------------------------------------------------------------------- /libraries/facebook/facebook.php: -------------------------------------------------------------------------------- 1 | initSharedSession(); 65 | 66 | // re-load the persisted state, since parent 67 | // attempted to read out of non-shared cookie 68 | $state = $this->getPersistentData('state'); 69 | if (!empty($state)) { 70 | $this->state = $state; 71 | } else { 72 | $this->state = null; 73 | } 74 | 75 | } 76 | } 77 | 78 | /** 79 | * Supported keys for persistent data 80 | * 81 | * @var array 82 | */ 83 | protected static $kSupportedKeys = 84 | array('state', 'code', 'access_token', 'user_id'); 85 | 86 | /** 87 | * Initiates Shared Session 88 | */ 89 | protected function initSharedSession() { 90 | $cookie_name = $this->getSharedSessionCookieName(); 91 | if (isset($_COOKIE[$cookie_name])) { 92 | $data = $this->parseSignedRequest($_COOKIE[$cookie_name]); 93 | if ($data && !empty($data['domain']) && 94 | self::isAllowedDomain($this->getHttpHost(), $data['domain'])) { 95 | // good case 96 | $this->sharedSessionID = $data['id']; 97 | return; 98 | } 99 | // ignoring potentially unreachable data 100 | } 101 | // evil/corrupt/missing case 102 | $base_domain = $this->getBaseDomain(); 103 | $this->sharedSessionID = md5(uniqid(mt_rand(), true)); 104 | $cookie_value = $this->makeSignedRequest( 105 | array( 106 | 'domain' => $base_domain, 107 | 'id' => $this->sharedSessionID, 108 | ) 109 | ); 110 | $_COOKIE[$cookie_name] = $cookie_value; 111 | if (!headers_sent()) { 112 | $expire = time() + self::FBSS_COOKIE_EXPIRE; 113 | setcookie($cookie_name, $cookie_value, $expire, '/', '.'.$base_domain); 114 | } else { 115 | // @codeCoverageIgnoreStart 116 | self::errorLog( 117 | 'Shared session ID cookie could not be set! You must ensure you '. 118 | 'create the Facebook instance before headers have been sent. This '. 119 | 'will cause authentication issues after the first request.' 120 | ); 121 | // @codeCoverageIgnoreEnd 122 | } 123 | } 124 | 125 | /** 126 | * Provides the implementations of the inherited abstract 127 | * methods. The implementation uses PHP sessions to maintain 128 | * a store for authorization codes, user ids, CSRF states, and 129 | * access tokens. 130 | */ 131 | 132 | /** 133 | * {@inheritdoc} 134 | * 135 | * @see BaseFacebook::setPersistentData() 136 | */ 137 | protected function setPersistentData($key, $value) { 138 | if (!in_array($key, self::$kSupportedKeys)) { 139 | self::errorLog('Unsupported key passed to setPersistentData.'); 140 | return; 141 | } 142 | 143 | $session_var_name = $this->constructSessionVariableName($key); 144 | $_SESSION[$session_var_name] = $value; 145 | } 146 | 147 | /** 148 | * {@inheritdoc} 149 | * 150 | * @see BaseFacebook::getPersistentData() 151 | */ 152 | protected function getPersistentData($key, $default = false) { 153 | if (!in_array($key, self::$kSupportedKeys)) { 154 | self::errorLog('Unsupported key passed to getPersistentData.'); 155 | return $default; 156 | } 157 | 158 | $session_var_name = $this->constructSessionVariableName($key); 159 | return isset($_SESSION[$session_var_name]) ? 160 | $_SESSION[$session_var_name] : $default; 161 | } 162 | 163 | /** 164 | * {@inheritdoc} 165 | * 166 | * @see BaseFacebook::clearPersistentData() 167 | */ 168 | protected function clearPersistentData($key) { 169 | if (!in_array($key, self::$kSupportedKeys)) { 170 | self::errorLog('Unsupported key passed to clearPersistentData.'); 171 | return; 172 | } 173 | 174 | $session_var_name = $this->constructSessionVariableName($key); 175 | if (isset($_SESSION[$session_var_name])) { 176 | unset($_SESSION[$session_var_name]); 177 | } 178 | } 179 | 180 | /** 181 | * {@inheritdoc} 182 | * 183 | * @see BaseFacebook::clearAllPersistentData() 184 | */ 185 | protected function clearAllPersistentData() { 186 | foreach (self::$kSupportedKeys as $key) { 187 | $this->clearPersistentData($key); 188 | } 189 | if ($this->sharedSessionID) { 190 | $this->deleteSharedSessionCookie(); 191 | } 192 | } 193 | 194 | /** 195 | * Deletes Shared session cookie 196 | */ 197 | protected function deleteSharedSessionCookie() { 198 | $cookie_name = $this->getSharedSessionCookieName(); 199 | unset($_COOKIE[$cookie_name]); 200 | $base_domain = $this->getBaseDomain(); 201 | setcookie($cookie_name, '', 1, '/', '.'.$base_domain); 202 | } 203 | 204 | /** 205 | * Returns the Shared session cookie name 206 | * 207 | * @return string The Shared session cookie name 208 | */ 209 | protected function getSharedSessionCookieName() { 210 | return self::FBSS_COOKIE_NAME . '_' . $this->getAppId(); 211 | } 212 | 213 | /** 214 | * Constructs and returns the name of the session key. 215 | * 216 | * @see setPersistentData() 217 | * @param string $key The key for which the session variable name to construct. 218 | * 219 | * @return string The name of the session key. 220 | */ 221 | protected function constructSessionVariableName($key) { 222 | $parts = array('fb', $this->getAppId(), $key); 223 | if ($this->sharedSessionID) { 224 | array_unshift($parts, $this->sharedSessionID); 225 | } 226 | return implode('_', $parts); 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /libraries/helper.class.php: -------------------------------------------------------------------------------- 1 | variables[$name] = $value; 11 | } 12 | 13 | /** Display Template **/ 14 | 15 | function render() { 16 | 17 | $backtrace = debug_backtrace(); 18 | $function = strtolower($backtrace[1]['function']); 19 | 20 | extract($this->variables); 21 | 22 | $contents = ''; 23 | 24 | if (file_exists(ROOT . DS . 'views' . DS . 'helpers' . DS . $function . '.php')) { 25 | $filename = (ROOT . DS . 'views' . DS . 'helpers' . DS . $function . '.php'); 26 | 27 | if (is_file($filename)) { 28 | ob_start(); 29 | include $filename; 30 | $contents = ob_get_contents(); 31 | ob_end_clean(); 32 | } 33 | } 34 | 35 | $this->variables = array(); 36 | 37 | return $contents; 38 | 39 | } 40 | 41 | 42 | 43 | } 44 | 45 | function getContent($filename) { 46 | 47 | return false; 48 | } 49 | -------------------------------------------------------------------------------- /libraries/pagination.class.php: -------------------------------------------------------------------------------- 1 | '', 9 | 'perpage' => '', 10 | 'page' => '', 11 | 'total' => '', 12 | 'numlinks' => '5', 13 | 'nexttext' => '>', 14 | 'prevtext' => '<', 15 | 'focusedclass' => 'selected', 16 | 'delimiter' => '', 17 | 'lasttext' => '>>', 18 | 'firsttext' => '<<', 19 | 'order' => 'votes', 20 | 'search' => '' 21 | ); 22 | 23 | function set($who,$what){ 24 | $this->output = ''; 25 | $this->options[$who] = $what; 26 | } 27 | 28 | function checkValues(){ 29 | $errors = array(); 30 | if($this->options['perpage']=='') $errors[] = 'Invalid perpage value'; 31 | if($this->options['page']=='') $errors[] = 'Invalid page value'; 32 | if($this->options['total']=='') $errors[] = 'Invalid total value'; 33 | if($this->options['numlinks']=='') $errors[] = 'Invalid numlinks value'; 34 | } 35 | function display($return = true){ 36 | $this->checkValues(); 37 | if($this->output=='') $this->generateOutput(); 38 | if(!$return) echo $this->output; 39 | else return $this->output; 40 | } 41 | function generateOutput(){ 42 | $elements = array(); 43 | $num_pages = ceil($this->options['total']/$this->options['perpage']); 44 | $front_links = ceil($this->options['numlinks']/2); 45 | $end_links = floor($this->options['numlinks']/2); 46 | if($this->options['page'] > $num_pages){ $this->set('page',1); } 47 | 48 | $start_page = max(1,($this->options['page']-$front_links+1)); 49 | $end_page = min($this->options['numlinks'] + $start_page-1,$num_pages); 50 | 51 | if($this->options['page'] > 1){ 52 | if ($num_pages > 10) { 53 | $elements[] = $this->generate_link(1,$this->options['firsttext']); 54 | } 55 | 56 | $elements[] = $this->generate_link($this->options['page']-1,$this->options['prevtext']); 57 | 58 | } 59 | 60 | for($i=$start_page;$i<=$end_page;$i++){ 61 | $elements[] = $this->generate_link($i); 62 | } 63 | 64 | if($this->options['page'] < $num_pages){ 65 | $elements[] = $this->generate_link($this->options['page']+1,$this->options['nexttext']); 66 | if ($num_pages > 10) { 67 | $elements[] = $this->generate_link($num_pages,$this->options['lasttext']); 68 | } 69 | } 70 | 71 | $this->output = implode($this->options['delimiter'],$elements); 72 | } 73 | function generate_link($page,$label=''){ 74 | $url = str_replace('%page%',$page,$this->options['urlscheme']); 75 | if($label=='') $label=$page; 76 | $html = "options['focusedclass']!='' && $page == $this->options['page'])?"class=\"{$this->options['focusedclass']}\" ":"")."href=\"{$url}\">{$label}"; 77 | return $html; 78 | } 79 | 80 | function displayOptions(){ 81 | if ($this->options['search']!='') { 82 | $elements[] = $this->generate_seclink('relevance'); 83 | } 84 | $elements[] = $this->generate_seclink('votes'); 85 | $elements[] = $this->generate_seclink('newest'); 86 | $elements[] = $this->generate_seclink('oldest'); 87 | return implode($this->options['delimiter'],$elements); 88 | 89 | } 90 | 91 | 92 | function generate_seclink($label){ 93 | $url = str_replace('%label%',$label,$this->options['urlscheme']); 94 | $html = "options['focusedclass']!='' && $label == $this->options['order'])?"class=\"{$this->options['focusedclass']}\" ":"")."href=\"{$url}\">{$label}"; 95 | return $html; 96 | } 97 | } -------------------------------------------------------------------------------- /libraries/postmark.class.php: -------------------------------------------------------------------------------- 1 | to('address@example.com', 'Name') 18 | * ->subject('Subject') 19 | * ->messagePlain('Plaintext message') 20 | * ->tag('Test tag') 21 | * ->send(); 22 | * 23 | * or: 24 | * 25 | * $email = new Mail_Postmark(); 26 | * $email->to('address@example.com', 'Name') 27 | * ->subject('Subject') 28 | * ->messagePlain('Plaintext message') 29 | * ->tag('Test tag') 30 | * ->send(); 31 | */ 32 | 33 | class Mail_Postmark 34 | { 35 | const DEBUG_OFF = 0; 36 | const DEBUG_VERBOSE = 1; 37 | const DEBUG_RETURN = 2; 38 | 39 | private $_fromName; 40 | private $_fromAddress; 41 | private $_tag; 42 | private $_toName; 43 | private $_toAddress; 44 | private $_replyToName; 45 | private $_replyToAddress; 46 | private $_cc = array(); 47 | private $_bcc = array(); 48 | private $_subject; 49 | private $_messagePlain; 50 | private $_messageHtml; 51 | private $_debugMode = self::DEBUG_OFF; 52 | 53 | /** 54 | * Initialize 55 | */ 56 | public function __construct() 57 | { 58 | $this->_default('POSTMARKAPP_MAIL_FROM_NAME', null); 59 | $this->_default('POSTMARKAPP_MAIL_FROM_ADDRESS', null); 60 | $this->_default('POSTMARKAPP_API_KEY', null); 61 | $this->from(POSTMARKAPP_MAIL_FROM_ADDRESS, POSTMARKAPP_MAIL_FROM_NAME)->messageHtml(null)->messagePlain(null); 62 | } 63 | 64 | /** 65 | * New e-mail 66 | * @return Mail_Postmark 67 | */ 68 | public static function compose() 69 | { 70 | return new self(); 71 | } 72 | 73 | /** 74 | * Turns debug output on 75 | * @param int $mode One of the debug constants 76 | * @return Mail_Postmark 77 | */ 78 | public function &debug($mode = self::DEBUG_VERBOSE) 79 | { 80 | $this->_debugMode = $mode; 81 | return $this; 82 | } 83 | 84 | /** 85 | * Specify sender. Overwrites default From. 86 | * @param string $address E-mail address used in From 87 | * @param string $name Optional. Name used in From 88 | * @return Mail_Postmark 89 | */ 90 | public function &from($address, $name = null) 91 | { 92 | $this->_fromAddress = $address; 93 | $this->_fromName = $name; 94 | return $this; 95 | } 96 | 97 | /** 98 | * Specify sender name. Overwrites default From name, but doesn't change address. 99 | * @param string $name Name used in From 100 | * @return Mail_Postmark 101 | */ 102 | public function &fromName($name) 103 | { 104 | $this->_fromName = $name; 105 | return $this; 106 | } 107 | 108 | /** 109 | * You can categorize outgoing email using the optional Tag property. 110 | * If you use different tags for the different types of emails your 111 | * application generates, you will be able to get detailed statistics 112 | * for them through the Postmark user interface. 113 | * Only 1 tag per mail is supported. 114 | * 115 | * @param string $tag One tag 116 | * @return Mail_Postmark 117 | */ 118 | public function &tag($tag) 119 | { 120 | $this->_tag = $tag; 121 | return $this; 122 | } 123 | 124 | /** 125 | * Specify receiver 126 | * @param string $address E-mail address used in To 127 | * @param string $name Optional. Name used in To 128 | * @return Mail_Postmark 129 | */ 130 | public function &to($address, $name = null) 131 | { 132 | $this->_toAddress = $address; 133 | $this->_toName = $name; 134 | return $this; 135 | } 136 | 137 | /** 138 | * Specify reply-to 139 | * @param string $address E-mail address used in To 140 | * @param string $name Optional. Name used in To 141 | * @return Mail_Postmark 142 | */ 143 | public function &replyTo($address, $name = null) 144 | { 145 | $this->_replyToAddress = $address; 146 | $this->_replyToName = $name; 147 | return $this; 148 | } 149 | 150 | /** 151 | * Add a CC address 152 | * @param string $address E-mail address used in CC 153 | * @param string $name Optional. Name used in CC 154 | * @return Mail_Postmark 155 | */ 156 | public function &addCC($address, $name = null) 157 | { 158 | $this->_cc[] = (is_null($name) ? $address : "$name <$address>"); 159 | return $this; 160 | } 161 | 162 | /** 163 | * Add a BCC address 164 | * @param string $address E-mail address used in BCC 165 | * @param string $name Optional. Name used in BCC 166 | * @return Mail_Postmark 167 | */ 168 | public function &addBCC($address, $name = null) 169 | { 170 | $this->_bcc[] = (is_null($name) ? $address : "$name <$address>"); 171 | return $this; 172 | } 173 | 174 | /** 175 | * Specify subject 176 | * @param string $subject E-mail subject 177 | * @return Mail_Postmark 178 | */ 179 | public function &subject($subject) 180 | { 181 | $this->_subject = $subject; 182 | return $this; 183 | } 184 | 185 | /** 186 | * Add plaintext message. Can be used in conjunction with messageHtml() 187 | * @param string $message E-mail message 188 | * @return Mail_Postmark 189 | */ 190 | public function &messagePlain($message) 191 | { 192 | $this->_messagePlain = $message; 193 | return $this; 194 | } 195 | 196 | /** 197 | * Add HTML message. Can be used in conjunction with messagePlain() 198 | * @param string $message E-mail message 199 | * @return Mail_Postmark 200 | */ 201 | public function &messageHtml($message) 202 | { 203 | $this->_messageHtml = $message; 204 | return $this; 205 | } 206 | 207 | /** 208 | * Sends the e-mail. Prints debug output if debug mode is turned on 209 | * @return Mail_Postmark 210 | */ 211 | public function &send() 212 | { 213 | if (is_null(POSTMARKAPP_API_KEY)) { 214 | throw new Exception('Postmark API key is not set'); 215 | } 216 | 217 | if (is_null($this->_fromAddress)) { 218 | throw new Exception('From address is not set'); 219 | } 220 | 221 | if (!isset($this->_toAddress)) { 222 | throw new Exception('To address is not set'); 223 | } 224 | 225 | if (!$this->_validateAddress($this->_fromAddress)) { 226 | throw new Exception("Invalid from address '{$this->_fromAddress}'"); 227 | } 228 | 229 | if (!$this->_validateAddress($this->_toAddress)) { 230 | throw new Exception("Invalid to address '{$this->_toAddress}'"); 231 | } 232 | 233 | if (isset($this->_replyToAddress) && !$this->_validateAddress($this->_replyToAddress)) { 234 | throw new Exception("Invalid reply to address '{$this->_replyToAddress}'"); 235 | } 236 | 237 | if (1 + count($this->_cc) + count($this->_bcc) > 20) { 238 | throw new Exception("Too many email recipients"); 239 | } 240 | 241 | $data = $this->_prepareData(); 242 | $headers = array( 243 | 'Accept: application/json', 244 | 'Content-Type: application/json', 245 | 'X-Postmark-Server-Token: ' . POSTMARKAPP_API_KEY 246 | ); 247 | 248 | $ch = curl_init(); 249 | curl_setopt($ch, CURLOPT_URL, 'http://api.postmarkapp.com/email'); 250 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 251 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); 252 | curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); 253 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 254 | 255 | $return = curl_exec($ch); 256 | 257 | if ($this->_debugMode == self::DEBUG_VERBOSE) { 258 | echo "JSON: " . json_encode($data) . "\nHeaders: \n\t" . implode("\n\t", $headers) . "\nReturn:\n$return"; 259 | 260 | } else if ($this->_debugMode == self::DEBUG_RETURN) { 261 | return array( 262 | 'json' => json_encode($data), 263 | 'headers' => $headers, 264 | 'return' => $return 265 | ); 266 | } 267 | 268 | if (curl_error($ch) != '') { 269 | throw new Exception(curl_error($ch)); 270 | } 271 | 272 | $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 273 | 274 | if (!$this->_isTwoHundred($httpCode)) { 275 | $message = json_decode($return)->Message; 276 | throw new Exception("Error while mailing. Postmark returned HTTP code $httpCode with message \"$message\""); 277 | } 278 | 279 | return $this; 280 | } 281 | 282 | /** 283 | * Prepares the data array 284 | */ 285 | private function _prepareData() 286 | { 287 | $data = array( 288 | 'Subject' => $this->_subject 289 | ); 290 | 291 | $data['From'] = is_null($this->_fromName) ? $this->_fromAddress : "{$this->_fromName} <{$this->_fromAddress}>"; 292 | $data['To'] = is_null($this->_toName) ? $this->_toAddress : "{$this->_toName} <{$this->_toAddress}>"; 293 | 294 | if (!is_null($this->_messageHtml)) { 295 | $data['HtmlBody'] = $this->_messageHtml; 296 | } 297 | 298 | if (!is_null($this->_messagePlain)) { 299 | $data['TextBody'] = $this->_messagePlain; 300 | } 301 | 302 | if (!is_null($this->_tag)) { 303 | $data['Tag'] = $this->_tag; 304 | } 305 | 306 | if (!is_null($this->_replyToAddress)) { 307 | $data['ReplyTo'] = is_null($this->_replyToName) ? $this->_replyToAddress : "{$this->_replyToName} <{$this->_replyToAddress}>"; 308 | } 309 | 310 | if (!empty($this->_cc)) { 311 | $data['Cc'] = implode(',',$this->_cc); 312 | } 313 | 314 | if (!empty($this->_bcc)) { 315 | $data['Bcc'] = implode(',',$this->_bcc); 316 | } 317 | 318 | return $data; 319 | } 320 | 321 | /** 322 | * If a number is 200-299 323 | */ 324 | private function _isTwoHundred($value) 325 | { 326 | return intval($value / 100) == 2; 327 | } 328 | 329 | /** 330 | * Defines a constant, if it isn't defined 331 | */ 332 | private function _default($name, $default) 333 | { 334 | if (!defined($name)) { 335 | define($name, $default); 336 | } 337 | } 338 | 339 | /** 340 | * Validates an e-mailadress 341 | */ 342 | private function _validateAddress($email) 343 | { 344 | // http://php.net/manual/en/function.filter-var.php 345 | return filter_var($email, FILTER_VALIDATE_EMAIL) !== false; 346 | } 347 | } -------------------------------------------------------------------------------- /libraries/recaptcha.php: -------------------------------------------------------------------------------- 1 | $value ) 50 | $req .= $key . '=' . urlencode( stripslashes($value) ) . '&'; 51 | 52 | // Cut the last '&' 53 | $req=substr($req,0,strlen($req)-1); 54 | return $req; 55 | } 56 | 57 | 58 | 59 | /** 60 | * Submits an HTTP POST to a reCAPTCHA server 61 | * @param string $host 62 | * @param string $path 63 | * @param array $data 64 | * @param int port 65 | * @return array response 66 | */ 67 | function _recaptcha_http_post($host, $path, $data, $port = 80) { 68 | 69 | $req = _recaptcha_qsencode ($data); 70 | 71 | $http_request = "POST $path HTTP/1.0\r\n"; 72 | $http_request .= "Host: $host\r\n"; 73 | $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n"; 74 | $http_request .= "Content-Length: " . strlen($req) . "\r\n"; 75 | $http_request .= "User-Agent: reCAPTCHA/PHP\r\n"; 76 | $http_request .= "\r\n"; 77 | $http_request .= $req; 78 | 79 | $response = ''; 80 | if( false == ( $fs = @fsockopen($host, $port, $errno, $errstr, 10) ) ) { 81 | die ('Could not open socket'); 82 | } 83 | 84 | fwrite($fs, $http_request); 85 | 86 | while ( !feof($fs) ) 87 | $response .= fgets($fs, 1160); // One TCP-IP packet 88 | fclose($fs); 89 | $response = explode("\r\n\r\n", $response, 2); 90 | 91 | return $response; 92 | } 93 | 94 | 95 | 96 | /** 97 | * Gets the challenge HTML (javascript and non-javascript version). 98 | * This is called from the browser, and the resulting reCAPTCHA HTML widget 99 | * is embedded within the HTML form it was called from. 100 | * @param string $pubkey A public key for reCAPTCHA 101 | * @param string $error The error given by reCAPTCHA (optional, default is null) 102 | * @param boolean $use_ssl Should the request be made over ssl? (optional, default is false) 103 | 104 | * @return string - The HTML to be embedded in the user's form. 105 | */ 106 | function recaptcha_get_html ($pubkey, $error = null, $use_ssl = false) 107 | { 108 | if ($pubkey == null || $pubkey == '') { 109 | die ("To use reCAPTCHA you must get an API key from http://recaptcha.net/api/getkey"); 110 | } 111 | 112 | if ($use_ssl) { 113 | $server = RECAPTCHA_API_SECURE_SERVER; 114 | } else { 115 | $server = RECAPTCHA_API_SERVER; 116 | } 117 | 118 | $errorpart = ""; 119 | if ($error) { 120 | $errorpart = "&error=" . $error; 121 | } 122 | return ' 123 | 124 | '; 129 | } 130 | 131 | 132 | 133 | 134 | /** 135 | * A ReCaptchaResponse is returned from recaptcha_check_answer() 136 | */ 137 | class ReCaptchaResponse { 138 | var $is_valid; 139 | var $error; 140 | } 141 | 142 | 143 | /** 144 | * Calls an HTTP POST function to verify if the user's guess was correct 145 | * @param string $privkey 146 | * @param string $remoteip 147 | * @param string $challenge 148 | * @param string $response 149 | * @param array $extra_params an array of extra variables to post to the server 150 | * @return ReCaptchaResponse 151 | */ 152 | function recaptcha_check_answer ($privkey, $remoteip, $challenge, $response, $extra_params = array()) 153 | { 154 | if ($privkey == null || $privkey == '') { 155 | die ("To use reCAPTCHA you must get an API key from http://recaptcha.net/api/getkey"); 156 | } 157 | 158 | if ($remoteip == null || $remoteip == '') { 159 | die ("For security reasons, you must pass the remote ip to reCAPTCHA"); 160 | } 161 | 162 | 163 | 164 | //discard spam submissions 165 | if ($challenge == null || strlen($challenge) == 0 || $response == null || strlen($response) == 0) { 166 | $recaptcha_response = new ReCaptchaResponse(); 167 | $recaptcha_response->is_valid = false; 168 | $recaptcha_response->error = 'incorrect-captcha-sol'; 169 | return $recaptcha_response; 170 | } 171 | 172 | $response = _recaptcha_http_post (RECAPTCHA_VERIFY_SERVER, "/verify", 173 | array ( 174 | 'privatekey' => $privkey, 175 | 'remoteip' => $remoteip, 176 | 'challenge' => $challenge, 177 | 'response' => $response 178 | ) + $extra_params 179 | ); 180 | 181 | $answers = explode ("\n", $response [1]); 182 | $recaptcha_response = new ReCaptchaResponse(); 183 | 184 | if (trim ($answers [0]) == 'true') { 185 | $recaptcha_response->is_valid = true; 186 | } 187 | else { 188 | $recaptcha_response->is_valid = false; 189 | $recaptcha_response->error = $answers [1]; 190 | } 191 | return $recaptcha_response; 192 | 193 | } 194 | 195 | /** 196 | * gets a URL where the user can sign up for reCAPTCHA. If your application 197 | * has a configuration page where you enter a key, you should provide a link 198 | * using this function. 199 | * @param string $domain The domain where the page is hosted 200 | * @param string $appname The name of your application 201 | */ 202 | function recaptcha_get_signup_url ($domain = null, $appname = null) { 203 | return "http://recaptcha.net/api/getkey?" . _recaptcha_qsencode (array ('domain' => $domain, 'app' => $appname)); 204 | } 205 | 206 | function _recaptcha_aes_pad($val) { 207 | $block_size = 16; 208 | $numpad = $block_size - (strlen ($val) % $block_size); 209 | return str_pad($val, strlen ($val) + $numpad, chr($numpad)); 210 | } 211 | 212 | /* Mailhide related code */ 213 | 214 | function _recaptcha_aes_encrypt($val,$ky) { 215 | if (! function_exists ("mcrypt_encrypt")) { 216 | die ("To use reCAPTCHA Mailhide, you need to have the mcrypt php module installed."); 217 | } 218 | $mode=MCRYPT_MODE_CBC; 219 | $enc=MCRYPT_RIJNDAEL_128; 220 | $val=_recaptcha_aes_pad($val); 221 | return mcrypt_encrypt($enc, $ky, $val, $mode, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); 222 | } 223 | 224 | 225 | function _recaptcha_mailhide_urlbase64 ($x) { 226 | return strtr(base64_encode ($x), '+/', '-_'); 227 | } 228 | 229 | /* gets the reCAPTCHA Mailhide url for a given email, public key and private key */ 230 | function recaptcha_mailhide_url($pubkey, $privkey, $email) { 231 | if ($pubkey == '' || $pubkey == null || $privkey == "" || $privkey == null) { 232 | die ("To use reCAPTCHA Mailhide, you have to sign up for a public and private key, " . 233 | "you can do so at http://mailhide.recaptcha.net/apikey"); 234 | } 235 | 236 | 237 | $ky = pack('H*', $privkey); 238 | $cryptmail = _recaptcha_aes_encrypt ($email, $ky); 239 | 240 | return "http://mailhide.recaptcha.net/d?k=" . $pubkey . "&c=" . _recaptcha_mailhide_urlbase64 ($cryptmail); 241 | } 242 | 243 | /** 244 | * gets the parts of the email to expose to the user. 245 | * eg, given johndoe@example,com return ["john", "example.com"]. 246 | * the email is then displayed as john...@example.com 247 | */ 248 | function _recaptcha_mailhide_email_parts ($email) { 249 | $arr = preg_split("/@/", $email ); 250 | 251 | if (strlen ($arr[0]) <= 4) { 252 | $arr[0] = substr ($arr[0], 0, 1); 253 | } else if (strlen ($arr[0]) <= 6) { 254 | $arr[0] = substr ($arr[0], 0, 3); 255 | } else { 256 | $arr[0] = substr ($arr[0], 0, 4); 257 | } 258 | return $arr; 259 | } 260 | 261 | /** 262 | * Gets html to display an email address given a public an private key. 263 | * to get a key, go to: 264 | * 265 | * http://mailhide.recaptcha.net/apikey 266 | */ 267 | function recaptcha_mailhide_html($pubkey, $privkey, $email) { 268 | $emailparts = _recaptcha_mailhide_email_parts ($email); 269 | $url = recaptcha_mailhide_url ($pubkey, $privkey, $email); 270 | 271 | return htmlentities($emailparts[0]) . "...@" . htmlentities ($emailparts [1]); 273 | 274 | } 275 | 276 | 277 | ?> 278 | -------------------------------------------------------------------------------- /libraries/template.class.php: -------------------------------------------------------------------------------- 1 | _controller = $controller; 10 | $this->_action = $action; 11 | } 12 | 13 | /** Set Variables **/ 14 | 15 | function set($name,$value) { 16 | $this->variables[$name] = $value; 17 | } 18 | 19 | function overrideController($controller) { 20 | $this->_controller = $controller; 21 | } 22 | 23 | function overrideAction($action) { 24 | $this->_action = $action; 25 | } 26 | 27 | /** Display Template **/ 28 | 29 | function render() { 30 | 31 | global $path; 32 | 33 | extract($this->variables); 34 | 35 | $controller = $this->_controller; 36 | $action = $this->_action; 37 | 38 | if (empty($noheader)) { 39 | 40 | 41 | include (ROOT . DS . 'views' . DS . 'header.php'); 42 | 43 | if (file_exists(ROOT . DS . 'views' . DS . $this->_controller . DS . 'header.php')) { 44 | include (ROOT . DS . 'views' . DS . $this->_controller . DS . 'header.php'); 45 | } 46 | } 47 | 48 | if (file_exists(ROOT . DS . 'views' . DS . $this->_controller . DS . $this->_action . '.php')) { 49 | include (ROOT . DS . 'views' . DS . $this->_controller . DS . $this->_action . '.php'); 50 | } 51 | 52 | if (empty($noheader)) { 53 | 54 | if (file_exists(ROOT . DS . 'views' . DS . $this->_controller . DS . 'footer.php')) { 55 | include (ROOT . DS . 'views' . DS . $this->_controller . DS . 'footer.php'); 56 | } 57 | 58 | include (ROOT . DS . 'views' . DS . 'footer.php'); 59 | 60 | 61 | } 62 | exit(); 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /libraries/timeago.php: -------------------------------------------------------------------------------- 1 | = 60 && $difference<60*60) 28 | { 29 | $interval = "n"; 30 | } 31 | 32 | // If difference is between 1 hour and 24 hours 33 | // hours is a good interval 34 | elseif($difference >= 60*60 && $difference<60*60*24) 35 | { 36 | $interval = "h"; 37 | } 38 | 39 | // If difference is between 1 day and 7 days 40 | // days is a good interval 41 | elseif($difference >= 60*60*24 && $difference<60*60*24*7) 42 | { 43 | $interval = "d"; 44 | } 45 | 46 | // If difference is between 1 week and 30 days 47 | // weeks is a good interval 48 | elseif($difference >= 60*60*24*7 && $difference < 49 | 60*60*24*30) 50 | { 51 | $interval = "ww"; 52 | } 53 | 54 | // If difference is between 30 days and 365 days 55 | // months is a good interval, again, the same thing 56 | // applies, if the 29th February happens to exist 57 | // between your 2 dates, the function will return 58 | // the 'incorrect' value for a day 59 | elseif($difference >= 60*60*24*30 && $difference < 60 | 60*60*24*365) 61 | { 62 | $interval = "m"; 63 | } 64 | 65 | // If difference is greater than or equal to 365 66 | // days, return year. This will be incorrect if 67 | // for example, you call the function on the 28th April 68 | // 2008 passing in 29th April 2007. It will return 69 | // 1 year ago when in actual fact (yawn!) not quite 70 | // a year has gone by 71 | elseif($difference >= 60*60*24*365) 72 | { 73 | $interval = "y"; 74 | } 75 | 76 | // Based on the interval, determine the 77 | // number of units between the two dates 78 | // From this point on, you would be hard 79 | // pushed telling the difference between 80 | // this function and DateDiff. If the $datediff 81 | // returned is 1, be sure to return the singular 82 | // of the unit, e.g. 'day' rather 'days' 83 | 84 | switch($interval) 85 | { 86 | case "m": 87 | $months_difference = floor($difference / 60 / 60 / 24 / 88 | 29); 89 | while (mktime(date("H", $datefrom), date("i", $datefrom), 90 | date("s", $datefrom), date("n", $datefrom)+($months_difference), 91 | date("j", $dateto), date("Y", $datefrom)) < $dateto) 92 | { 93 | $months_difference++; 94 | } 95 | $datediff = $months_difference; 96 | 97 | // We need this in here because it is possible 98 | // to have an 'm' interval and a months 99 | // difference of 12 because we are using 29 days 100 | // in a month 101 | 102 | if($datediff==12) 103 | { 104 | $datediff--; 105 | } 106 | 107 | $res = ($datediff==1) ? "$datediff mnth" : "$datediff 108 | mnths"; 109 | $res = datifyunix($datefrom); 110 | break; 111 | 112 | case "y": 113 | $datediff = floor($difference / 60 / 60 / 24 / 365); 114 | $res = ($datediff==1) ? "$datediff yr" : "$datediff 115 | yrs"; 116 | 117 | $res = datifyunix($datefrom); 118 | break; 119 | 120 | case "d": 121 | $datediff = floor($difference / 60 / 60 / 24); 122 | $res = ($datediff==1) ? "$datediff day ago" : "$datediff 123 | days ago"; 124 | break; 125 | 126 | case "ww": 127 | $datediff = floor($difference / 60 / 60 / 24 / 7); 128 | $res = ($datediff==1) ? "$datediff wk" : "$datediff 129 | wks"; 130 | $res = datifyunix($datefrom); 131 | break; 132 | 133 | case "h": 134 | $datediff = floor($difference / 60 / 60); 135 | $res = ($datediff==1) ? "$datediff hr ago" : "$datediff 136 | hrs ago"; 137 | break; 138 | 139 | case "n": 140 | $datediff = floor($difference / 60); 141 | $res = ($datediff==1) ? "$datediff min ago" : 142 | "$datediff mins ago"; 143 | break; 144 | 145 | case "s": 146 | $datediff = $difference; 147 | $res = ($datediff==1) ? "$datediff sec ago" : 148 | "$datediff secs ago"; 149 | break; 150 | } 151 | return $res; 152 | } 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | function TimeHours($datefrom,$dateto=-1) 161 | { 162 | // Defaults and assume if 0 is passed in that 163 | // its an error rather than the epoch 164 | 165 | if($dateto==-1) { $dateto = time(); } 166 | 167 | 168 | // Calculate the difference in seconds betweeen 169 | // the two timestamps 170 | $difference = $dateto - $datefrom; 171 | 172 | if ($difference < 0 ) { 173 | return 'n/a'; 174 | } 175 | // If difference is less than 60 seconds, 176 | // seconds is a good interval of choice 177 | 178 | if($difference < 60) 179 | { 180 | $interval = "s"; 181 | } 182 | 183 | // If difference is between 60 seconds and 184 | // 60 minutes, minutes is a good interval 185 | elseif($difference >= 60 && $difference<60*60) 186 | { 187 | $interval = "n"; 188 | } 189 | 190 | // If difference is between 1 hour and 24 hours 191 | // hours is a good interval 192 | elseif($difference >= 60*60 ) 193 | { 194 | $interval = "h"; 195 | } 196 | 197 | switch($interval) 198 | { 199 | case "m": 200 | $months_difference = floor($difference / 60 / 60 / 24 / 201 | 29); 202 | while (mktime(date("H", $datefrom), date("i", $datefrom), 203 | date("s", $datefrom), date("n", $datefrom)+($months_difference), 204 | date("j", $dateto), date("Y", $datefrom)) < $dateto) 205 | { 206 | $months_difference++; 207 | } 208 | $datediff = $months_difference; 209 | 210 | // We need this in here because it is possible 211 | // to have an 'm' interval and a months 212 | // difference of 12 because we are using 29 days 213 | // in a month 214 | 215 | if($datediff==12) 216 | { 217 | $datediff--; 218 | } 219 | 220 | $res = ($datediff==1) ? "$datediff mnth" : "$datediff 221 | mnths"; 222 | break; 223 | 224 | case "y": 225 | $datediff = floor($difference / 60 / 60 / 24 / 365); 226 | $res = ($datediff==1) ? "$datediff yr" : "$datediff 227 | yrs"; 228 | break; 229 | 230 | case "d": 231 | $datediff = floor($difference / 60 / 60 / 24); 232 | $res = ($datediff==1) ? "$datediff day" : "$datediff 233 | days"; 234 | break; 235 | 236 | case "ww": 237 | $datediff = floor($difference / 60 / 60 / 24 / 7); 238 | $res = ($datediff==1) ? "$datediff wk" : "$datediff 239 | wks"; 240 | break; 241 | 242 | case "h": 243 | $datediff = floor($difference / 60 / 60); 244 | $res = ($datediff==1) ? "$datediff hr" : "$datediff 245 | hrs"; 246 | break; 247 | 248 | case "n": 249 | $datediff = floor($difference / 60); 250 | $res = ($datediff==1) ? "$datediff min" : 251 | "$datediff mins"; 252 | break; 253 | 254 | case "s": 255 | $datediff = $difference; 256 | $res = ($datediff==1) ? "$datediff sec" : 257 | "$datediff secs"; 258 | break; 259 | } 260 | return $res; 261 | } 262 | -------------------------------------------------------------------------------- /libraries/twitter/twitter.php: -------------------------------------------------------------------------------- 1 | http_status; } 54 | function lastAPICall() { return $this->last_api_call; } 55 | 56 | /** 57 | * construct TwitterOAuth object 58 | */ 59 | function __construct($consumer_key, $consumer_secret, $oauth_token = NULL, $oauth_token_secret = NULL) { 60 | $this->sha1_method = new OAuthSignatureMethod_HMAC_SHA1(); 61 | $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret); 62 | if (!empty($oauth_token) && !empty($oauth_token_secret)) { 63 | $this->token = new OAuthConsumer($oauth_token, $oauth_token_secret); 64 | } else { 65 | $this->token = NULL; 66 | } 67 | } 68 | 69 | 70 | /** 71 | * Get a request_token from Twitter 72 | * 73 | * @returns a key/value array containing oauth_token and oauth_token_secret 74 | */ 75 | function getRequestToken($oauth_callback) { 76 | $parameters = array(); 77 | $parameters['oauth_callback'] = $oauth_callback; 78 | $request = $this->oAuthRequest($this->requestTokenURL(), 'GET', $parameters); 79 | $token = OAuthUtil::parse_parameters($request); 80 | $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); 81 | return $token; 82 | } 83 | 84 | /** 85 | * Get the authorize URL 86 | * 87 | * @returns a string 88 | */ 89 | function getAuthorizeURL($token, $sign_in_with_twitter = TRUE) { 90 | if (is_array($token)) { 91 | $token = $token['oauth_token']; 92 | } 93 | if (empty($sign_in_with_twitter)) { 94 | return $this->authorizeURL() . "?oauth_token={$token}"; 95 | } else { 96 | return $this->authenticateURL() . "?oauth_token={$token}"; 97 | } 98 | } 99 | 100 | /** 101 | * Exchange request token and secret for an access token and 102 | * secret, to sign API calls. 103 | * 104 | * @returns array("oauth_token" => "the-access-token", 105 | * "oauth_token_secret" => "the-access-secret", 106 | * "user_id" => "9436992", 107 | * "screen_name" => "abraham") 108 | */ 109 | function getAccessToken($oauth_verifier) { 110 | $parameters = array(); 111 | $parameters['oauth_verifier'] = $oauth_verifier; 112 | $request = $this->oAuthRequest($this->accessTokenURL(), 'GET', $parameters); 113 | $token = OAuthUtil::parse_parameters($request); 114 | $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); 115 | return $token; 116 | } 117 | 118 | /** 119 | * One time exchange of username and password for access token and secret. 120 | * 121 | * @returns array("oauth_token" => "the-access-token", 122 | * "oauth_token_secret" => "the-access-secret", 123 | * "user_id" => "9436992", 124 | * "screen_name" => "abraham", 125 | * "x_auth_expires" => "0") 126 | */ 127 | function getXAuthToken($username, $password) { 128 | $parameters = array(); 129 | $parameters['x_auth_username'] = $username; 130 | $parameters['x_auth_password'] = $password; 131 | $parameters['x_auth_mode'] = 'client_auth'; 132 | $request = $this->oAuthRequest($this->accessTokenURL(), 'POST', $parameters); 133 | $token = OAuthUtil::parse_parameters($request); 134 | $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); 135 | return $token; 136 | } 137 | 138 | /** 139 | * GET wrapper for oAuthRequest. 140 | */ 141 | function get($url, $parameters = array()) { 142 | $response = $this->oAuthRequest($url, 'GET', $parameters); 143 | if ($this->format === 'json' && $this->decode_json) { 144 | return json_decode($response); 145 | } 146 | return $response; 147 | } 148 | 149 | /** 150 | * POST wrapper for oAuthRequest. 151 | */ 152 | function post($url, $parameters = array()) { 153 | $response = $this->oAuthRequest($url, 'POST', $parameters); 154 | if ($this->format === 'json' && $this->decode_json) { 155 | return json_decode($response); 156 | } 157 | return $response; 158 | } 159 | 160 | /** 161 | * DELETE wrapper for oAuthReqeust. 162 | */ 163 | function delete($url, $parameters = array()) { 164 | $response = $this->oAuthRequest($url, 'DELETE', $parameters); 165 | if ($this->format === 'json' && $this->decode_json) { 166 | return json_decode($response); 167 | } 168 | return $response; 169 | } 170 | 171 | /** 172 | * Format and sign an OAuth / API request 173 | */ 174 | function oAuthRequest($url, $method, $parameters) { 175 | if (strrpos($url, 'https://') !== 0 && strrpos($url, 'http://') !== 0) { 176 | $url = "{$this->host}{$url}.{$this->format}"; 177 | } 178 | $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $parameters); 179 | $request->sign_request($this->sha1_method, $this->consumer, $this->token); 180 | switch ($method) { 181 | case 'GET': 182 | return $this->http($request->to_url(), 'GET'); 183 | default: 184 | return $this->http($request->get_normalized_http_url(), $method, $request->to_postdata()); 185 | } 186 | } 187 | 188 | /** 189 | * Make an HTTP request 190 | * 191 | * @return API results 192 | */ 193 | function http($url, $method, $postfields = NULL) { 194 | $this->http_info = array(); 195 | $ci = curl_init(); 196 | /* Curl settings */ 197 | curl_setopt($ci, CURLOPT_USERAGENT, $this->useragent); 198 | curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout); 199 | curl_setopt($ci, CURLOPT_TIMEOUT, $this->timeout); 200 | curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE); 201 | curl_setopt($ci, CURLOPT_HTTPHEADER, array('Expect:')); 202 | curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer); 203 | curl_setopt($ci, CURLOPT_HEADERFUNCTION, array($this, 'getHeader')); 204 | curl_setopt($ci, CURLOPT_HEADER, FALSE); 205 | 206 | switch ($method) { 207 | case 'POST': 208 | curl_setopt($ci, CURLOPT_POST, TRUE); 209 | if (!empty($postfields)) { 210 | curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields); 211 | } 212 | break; 213 | case 'DELETE': 214 | curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'DELETE'); 215 | if (!empty($postfields)) { 216 | $url = "{$url}?{$postfields}"; 217 | } 218 | } 219 | 220 | curl_setopt($ci, CURLOPT_URL, $url); 221 | $response = curl_exec($ci); 222 | $this->http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE); 223 | $this->http_info = array_merge($this->http_info, curl_getinfo($ci)); 224 | $this->url = $url; 225 | curl_close ($ci); 226 | return $response; 227 | } 228 | 229 | /** 230 | * Get the header info to store. 231 | */ 232 | function getHeader($ch, $header) { 233 | $i = strpos($header, ':'); 234 | if (!empty($i)) { 235 | $key = str_replace('-', '_', strtolower(substr($header, 0, $i))); 236 | $value = trim(substr($header, $i + 2)); 237 | $this->http_header[$key] = $value; 238 | } 239 | return strlen($header); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /libraries/uaparser.class.php: -------------------------------------------------------------------------------- 1 | regexes 42 | */ 43 | public function __construct($customRegexesFile = null) { 44 | 45 | $regexesFile = ($customRegexesFile !== null) ? $customRegexesFile : dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'data/regexes.json'; 46 | if (file_exists($regexesFile)) { 47 | $this->regexes = json_decode(file_get_contents($regexesFile)); 48 | } else { 49 | $title = 'Error loading ua-parser'; 50 | if ($customRegexesFile !== null) { 51 | $message = 'ua-parser can\'t find the custom regexes file you supplied ('.$customRegexesFile.'). Please make sure you have the correct path.'; 52 | } else { 53 | $message = 'Please download the regexes.json file before using uaparser.php.'; 54 | if ( php_sapi_name() == 'cli' ) { 55 | $message .= ' (php uaparser-cli.php -g)'; 56 | } 57 | } 58 | 59 | throw new FileNotFound_Exception($message); 60 | } 61 | } 62 | 63 | /** 64 | * Sets up some standard variables as well as starts the user agent parsing process 65 | * @param string a user agent string to test, defaults to an empty string 66 | * @return object the result of the user agent parsing 67 | */ 68 | public function parse($ua = '') { 69 | 70 | // build the default obj that will be returned 71 | $result = (object) array( 72 | 'ua' => (object) array(), 73 | 'os' => (object) array(), 74 | 'device' => (object) array(), 75 | 'toFullString' => '', 76 | 'uaOriginal' => $ua 77 | ); 78 | 79 | // figure out the ua, os, and device properties if possible 80 | $result->ua = $this->uaParse($ua); 81 | $result->os = $this->osParse($ua); 82 | $result->device = $this->deviceParse($ua); 83 | 84 | // create a full string version based on the ua and os objects 85 | $result->toFullString = $this->toFullString($result->ua, $result->os); 86 | 87 | // log the results when testing 88 | if ($this->log) { 89 | $this->log($result); 90 | } 91 | 92 | return $result; 93 | 94 | } 95 | 96 | /** 97 | * Attempts to see if the user agent matches a user_agents_parsers regex from regexes.json 98 | * @param string a user agent string to test 99 | * @return object the result of the user agent parsing 100 | */ 101 | public function uaParse($uaString = '') { 102 | 103 | // build the default obj that will be returned 104 | $ua = (object) array( 105 | 'family' => 'Other', 106 | 'major' => null, 107 | 'minor' => null, 108 | 'patch' => null, 109 | 'toString' => '', 110 | 'toVersionString' => '' 111 | ); 112 | 113 | // run the regexes to match things up 114 | $uaRegexes = $this->regexes->user_agent_parsers; 115 | foreach ($uaRegexes as $uaRegex) { 116 | 117 | // tests the supplied regex against the user agent 118 | if (preg_match('/'.str_replace('/','\/',str_replace('\/','/',$uaRegex->regex)).'/i',$uaString,$matches)) { 119 | 120 | // Make sure matches are at least set to null or Other 121 | if (!isset($matches[1])) { $matches[1] = 'Other'; } 122 | if (!isset($matches[2])) { $matches[2] = null; } 123 | if (!isset($matches[3])) { $matches[3] = null; } 124 | if (!isset($matches[4])) { $matches[4] = null; } 125 | 126 | // ua name 127 | $ua->family = isset($uaRegex->family_replacement) ? str_replace('$1',$matches[1],$uaRegex->family_replacement) : $matches[1]; 128 | 129 | // version properties 130 | $ua->major = isset($uaRegex->v1_replacement) ? $uaRegex->v1_replacement : $matches[2]; 131 | $ua->minor = isset($uaRegex->v2_replacement) ? $uaRegex->v2_replacement : $matches[3]; 132 | $ua->patch = isset($uaRegex->v3_replacement) ? $uaRegex->v3_replacement : $matches[4]; 133 | 134 | // extra strings 135 | $ua->toString = $this->toString($ua); 136 | $ua->toVersionString = $this->toVersionString($ua); 137 | 138 | return $ua; 139 | } 140 | 141 | } 142 | 143 | return $ua; 144 | 145 | } 146 | 147 | /** 148 | * Attempts to see if the user agent matches an os_parsers regex from regexes.json 149 | * @param string a user agent string to test 150 | * @return object the result of the os parsing 151 | */ 152 | public function osParse($uaString = '') { 153 | 154 | // build the default obj that will be returned 155 | $os = (object) array( 156 | 'family' => 'Other', 157 | 'major' => null, 158 | 'minor' => null, 159 | 'patch' => null, 160 | 'patch_minor' => null, 161 | 'toString' => '', 162 | 'toVersionString' => '' 163 | ); 164 | 165 | // run the regexes to match things up 166 | $osRegexes = $this->regexes->os_parsers; 167 | foreach ($osRegexes as $osRegex) { 168 | 169 | if (preg_match('/'.str_replace('/','\/',str_replace('\/','/',$osRegex->regex)).'/i',$uaString,$matches)) { 170 | 171 | // Make sure matches are at least set to null or Other 172 | if (!isset($matches[1])) { $matches[1] = 'Other'; } 173 | if (!isset($matches[2])) { $matches[2] = null; } 174 | if (!isset($matches[3])) { $matches[3] = null; } 175 | if (!isset($matches[4])) { $matches[4] = null; } 176 | if (!isset($matches[5])) { $matches[5] = null; } 177 | 178 | // os name 179 | $os->family = isset($osRegex->os_replacement) ? $osRegex->os_replacement : $matches[1]; 180 | 181 | // version properties 182 | $os->major = isset($osRegex->os_v1_replacement) ? $osRegex->os_v1_replacement : $matches[2]; 183 | $os->minor = isset($osRegex->os_v2_replacement) ? $osRegex->os_v2_replacement : $matches[3]; 184 | $os->patch = isset($osRegex->os_v3_replacement) ? $osRegex->os_v3_replacement : $matches[4]; 185 | $os->patch_minor = isset($osRegex->os_v4_replacement) ? $osRegex->os_v4_replacement : $matches[5]; 186 | 187 | // extra strings 188 | $os->toString = $this->toString($os); 189 | $os->toVersionString = $this->toVersionString($os); 190 | 191 | return $os; 192 | } 193 | 194 | } 195 | 196 | return $os; 197 | 198 | } 199 | 200 | /** 201 | * Attempts to see if the user agent matches a device_parsers regex from regexes.json 202 | * @param string a user agent string to test 203 | * @return object the result of the device parsing 204 | */ 205 | public function deviceParse($uaString = '') { 206 | 207 | // build the default obj that will be returned 208 | $device = (object) array( 209 | 'family' => 'Other' 210 | ); 211 | 212 | // run the regexes to match things up 213 | $deviceRegexes = $this->regexes->device_parsers; 214 | foreach ($deviceRegexes as $deviceRegex) { 215 | 216 | if (preg_match('/'.str_replace('/','\/',str_replace('\/','/',$deviceRegex->regex)).'/i',$uaString,$matches)) { 217 | 218 | // Make sure matches are at least set to null or Other 219 | if (!isset($matches[1])) { $matches[1] = 'Other'; } 220 | 221 | // device name 222 | $device->family = isset($deviceRegex->device_replacement) ? str_replace('$1',str_replace("_"," ",$matches[1]),$deviceRegex->device_replacement) : str_replace("_"," ",$matches[1]); 223 | 224 | return $device; 225 | 226 | } 227 | 228 | } 229 | 230 | return $device; 231 | 232 | } 233 | 234 | /** 235 | * Returns a string consisting of the family and full version number based on the provided type 236 | * @param object the object (ua or os) to be used 237 | * @return string the result of combining family and version 238 | */ 239 | public function toString($obj) { 240 | 241 | $versionString = $this->toVersionString($obj); 242 | $string = !empty($versionString) ? $obj->family.' '.$versionString : $obj->family; 243 | 244 | return $string; 245 | } 246 | 247 | /** 248 | * Returns a string consisting of just the full version number based on the provided type 249 | * @param object the obj that contains version number bits 250 | * @return string the result of combining the version number bits together 251 | */ 252 | public function toVersionString($obj) { 253 | 254 | $versionString = isset($obj->major) ? $obj->major : ''; 255 | $versionString = isset($obj->minor) ? $versionString.'.'.$obj->minor : $versionString; 256 | $versionString = isset($obj->patch) ? $versionString.'.'.$obj->patch : $versionString; 257 | $versionString = isset($obj->patch_minor) ? $versionString.'.'.$obj->patch_minor : $versionString; 258 | 259 | return $versionString; 260 | 261 | } 262 | 263 | /** 264 | * Returns a string consistig of the family and full version number for both the browser and os 265 | * @param object the ua object 266 | * @param object the os object 267 | * @return string the result of combining family and version 268 | */ 269 | public function toFullString($ua,$os) { 270 | 271 | $fullString = $this->toString($ua).'/'.$this->toString($os); 272 | 273 | return $fullString; 274 | 275 | } 276 | 277 | /** 278 | * Logs the user agent info 279 | */ 280 | protected function log($data) { 281 | $jsonData = json_encode($data); 282 | $fp = fopen(dirname(__FILE__).DIRECTORY_SEPARATOR.'log/user_agents.log', 'a'); 283 | fwrite($fp, $jsonData."\r\n"); 284 | fclose($fp); 285 | } 286 | 287 | } 288 | 289 | class FileNotFound_Exception extends Exception {} -------------------------------------------------------------------------------- /views/connect/facebook.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |

    Choose a Page

    5 |

    Connect any of your Facebook pages to add to SocialTurn.

    6 |
    7 |
    8 |
    9 | 10 | 11 | 12 | 13 | 26 | 27 | 28 | 29 |
    30 |

    Sorry, we are unable to find any pages associated with your account.

    31 |
    32 | 33 | 34 |
    35 |
    -------------------------------------------------------------------------------- /views/connect/index.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |

    Connect a Social Network

    5 |

    Share to many different places with SocialTurn and we make sure your posts look great everywhere. Click the 'Connect' buttons below to begin connecting your account to SocialPost:

    6 |
    7 |
    8 | 9 |
    10 | 27 | 28 | 45 | 46 |
    47 |
    48 | 49 |
    50 | 51 |
    52 |
    53 |

    google

    54 |
    55 |
    56 | Connect 57 |
    58 | 59 | 60 |
    61 |
    62 | 63 |
    64 | 65 | 66 | 67 |
    68 |
    69 |

    Connected Networks

    70 |

    Re-connect or delete your existing networks

    71 |
    72 |
    73 | 74 |
    75 | 76 | 77 |
    78 |
    79 |
    80 | 81 |
    82 |
    83 |
    ()
    84 | Reconnect | Delete 85 |
    86 |
    87 |
    88 | 89 | 90 |
    91 | 92 | 93 |
    -------------------------------------------------------------------------------- /views/footer.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 27 | 28 |
    29 |

    30 |
    31 | 36 | 37 | 38 | 52 | 53 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /views/header.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Publish to Social Media for Free | SocialTurn 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 66 | -------------------------------------------------------------------------------- /views/oops/noaccounts.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 |

    Oops!

    6 |

    Sorry, no accounts found as yet. You will have to wait till the account owner activates a few accounts for you.

    7 |
    8 | 9 | 12 | 13 | 14 |
    15 |
    -------------------------------------------------------------------------------- /views/oops/notfound.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 |

    Oops!

    6 |

    Sorry, we are unable to find this page

    7 |
    8 | 9 | 12 | 13 | 14 |
    15 |
    -------------------------------------------------------------------------------- /views/oops/permissions.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 |

    Oops!

    6 |

    Sorry, you do not have sufficient privileges to access this page

    7 |
    8 | 9 | 12 | 13 | 14 |
    15 |
    -------------------------------------------------------------------------------- /views/social/footer.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | -------------------------------------------------------------------------------- /views/social/header.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 | 6 | 7 |   8 | 9 | 10 |
    11 | 12 | 13 | 14 |
    15 |
    16 |
    17 |
    18 | 25 | 26 |
    27 |
    28 | -------------------------------------------------------------------------------- /views/social/manual.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Share

    4 |

    Add your own update and image to multiple accounts. You can also send it to queue if you do not want to send it immediately.

    5 |
    6 |
    7 | 8 |
    9 |
    10 |
    11 |

    Select which accounts you would like to post to?

    12 | 17 |
    18 | 19 | 20 |
    21 | 22 |
    23 |
    24 | 25 |
    26 |
    27 |
    28 |
    29 | 30 |
    31 |
    32 |
    33 | Select imageChange 34 | Remove 35 |
    36 |
    37 | 38 |
    39 |
    40 |
    41 |
    42 | 43 | 44 |
    45 |
    46 | 47 |
    48 | -------------------------------------------------------------------------------- /views/social/post.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |

    Yay!

    5 |

    Your posts are on their way and will appear shortly!

    6 |
    7 |
    8 |
    -------------------------------------------------------------------------------- /views/social/queue.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Queue

    4 |

    We'll make sure your posts are sent out even when you're asleep! Here is a schedule of all posts that have been queued. All timings are GMT +.

    5 |
    6 |
    7 | 8 | 9 |
    10 |
    11 |
    Failed  
    12 | 13 | 14 | 15 | 1):?>
    16 | 17 |
    18 |
    19 | 20 |
    21 |
    22 |
    23 | -------------------------------------------------------------------------------- /views/social/schedule.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Schedule

    4 |

    So, when would you like your posts to be sent? Choose your timezone, schedule your times and we'll make sure your posts are sent out even when you're asleep!

    5 |
    6 |
    7 | 8 |
    9 | 10 |
    11 |
    12 | 13 |

    Select your timezone (can be different from your current location)

    14 | 47 | 48 |
    49 |
    50 | 51 |
    52 |
    53 |

    Which days would you like to post?

    54 | 63 | 64 |
    65 |
    66 | 67 |
    68 |
    69 |

    What time would you like to post each selected day?

    70 | 96 | 97 |
    98 |
    99 |
    100 |
    101 | 102 |
    103 |
    104 | 105 |
    106 | -------------------------------------------------------------------------------- /views/social/suggestions.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Suggestions

    4 |

    Running out of ideas? Some curated suggestions which you can use

    5 |
    6 |
    7 |
    8 | 9 | 13 | 19 |
    20 |
    21 |
    22 | 23 | 24 | 25 |
    26 |
    27 |
    28 |
    29 | 30 | 31 | 32 |
    via @
    33 |
    34 |
    35 | 36 | 37 | 38 | 39 | 40 | 41 |
    42 |
    43 |
    44 | -------------------------------------------------------------------------------- /views/team/index.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |

    Manage your team

    5 |

    Assign privileges to team members and manage them or invite a new member

    6 |
    7 |
    8 | New Team Member 9 |
    10 |
    11 |
    12 | 13 |
    14 |
    15 | 16 | 17 | 18 |
    19 |
    20 |
    21 | 22 |
    23 |
    24 |
    25 | OwnerAdministratorTeam Member 1):?> | Manage 26 |
    27 |
    28 |
    29 | 30 | 31 | 32 |
    33 |
    -------------------------------------------------------------------------------- /views/team/invite.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |

    Invite a team member

    5 |

    Enter the email address of the team member you would like to invite. All team members will be invited with no privileges.

    6 |
    7 |
    8 |
    9 | 10 | 11 |
    12 |
    13 | 14 |
    15 | 16 |
    17 | 18 |
    19 | 20 |
    21 | 22 |
    23 | 24 | 25 |
    26 |
    27 |
    -------------------------------------------------------------------------------- /views/team/invited.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |

    has been invited!

    5 |

    Below is the unique link that they must use to register or the member won't be added to your account.

    6 |
    7 |
    8 |
    9 | 10 |
    11 |
    12 |
    13 |
    users/invite//
    14 |
    15 |
    16 |
    -------------------------------------------------------------------------------- /views/team/manage.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |

    Manage -

    5 |

    You can manage permissions for each user and their roles.

    6 |
    7 |
    8 |
    9 | 10 | 11 |
    12 |
    13 | 14 |
    15 | 16 |
    17 |
    User Role
    18 | 22 |
    23 | 24 |
    25 |
    User can add updates to which channels?
    26 | 27 | 39 |
    40 | 41 |
    42 | 43 | 44 |
    45 | 46 | 47 |
    48 | 49 |
    50 | 51 | 52 | 53 |
    54 |
    55 |
    -------------------------------------------------------------------------------- /views/users/inform.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 |

    Yay!

    6 |

    We have informed the owner. You will receive an email as soon as they add a few accounts for you to manage.

    7 |
    8 | 9 | 12 | 13 | 14 |
    15 |
    -------------------------------------------------------------------------------- /views/users/invite.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 |

    You've been invited

    6 |

    Join the team and share updates, together

    7 |
    8 | 9 |
    10 | 11 |
    12 |
    13 | 14 |
    15 | 16 |
    17 | 18 | 19 | 20 |
    21 | 22 | 23 |
    24 |
    -------------------------------------------------------------------------------- /views/users/login.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 |

    SocialTurn

    6 |

    The easiest way to publish, for teams

    7 |
    8 | 9 |
    10 | 11 |
    12 |
    13 | 14 |
    15 | 16 |
    17 | 18 |
    19 | 22 | 23 | 24 |
    25 |
    -------------------------------------------------------------------------------- /views/users/register.php: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 |

    SocialTurn

    6 |

    The easiest way to publish, for teams

    7 |
    8 | 9 |
    10 | 11 |
    12 |
    13 | 14 |
    15 |
    16 | 17 |
    18 | 19 |
    20 | 21 |
    22 | 25 | 26 | 27 |
    28 |
    --------------------------------------------------------------------------------