diff --git a/composer.json b/composer.json index 52b4f2c867..5cb33e67d6 100644 --- a/composer.json +++ b/composer.json @@ -30,6 +30,7 @@ "bower-asset/base64": "^1.0", "bower-asset/Chart-js": "^2.7", "bower-asset/perfect-scrollbar": "^0.6", + "bower-asset/vue": "^2.5", "npm-asset/jquery": "^2.0", "npm-asset/jquery-colorbox": "^1.6", "npm-asset/jquery-datetimepicker": "^2.4.0", diff --git a/composer.lock b/composer.lock index 28199f4f51..34362cb1bb 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "12b8df66213734281765cb6e2c5a786e", + "content-hash": "96062c2020a40f14b52e5e91c79995a7", "packages": [ { "name": "asika/simple-console", @@ -133,6 +133,22 @@ "description": "Minimalistic but perfect custom scrollbar plugin", "time": "2017-01-10T01:04:09+00:00" }, + { + "name": "bower-asset/vue", + "version": "v2.5.16", + "source": { + "type": "git", + "url": "https://github.com/vuejs/vue.git", + "reference": "25342194016dc3bcc81cb3e8e229b0fb7ba1d1d6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vuejs/vue/zipball/25342194016dc3bcc81cb3e8e229b0fb7ba1d1d6", + "reference": "25342194016dc3bcc81cb3e8e229b0fb7ba1d1d6", + "shasum": "" + }, + "type": "bower-asset-library" + }, { "name": "divineomega/password_exposed", "version": "v2.5.1", diff --git a/include/conversation.php b/include/conversation.php index 8a2887d6b7..41f10959b2 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -668,33 +668,7 @@ function conversation(App $a, $items, $mode, $update, $preview = false, $order = $profile_name = $item['author-link']; } - $tags = []; - $hashtags = []; - $mentions = []; - - $searchpath = System::baseUrl()."/search?tag="; - - $taglist = dba::select('term', ['type', 'term', 'url'], - ["`otype` = ? AND `oid` = ? AND `type` IN (?, ?)", TERM_OBJ_POST, $item['id'], TERM_HASHTAG, TERM_MENTION], - ['order' => ['tid']]); - - while ($tag = dba::fetch($taglist)) { - if ($tag["url"] == "") { - $tag["url"] = $searchpath . strtolower($tag["term"]); - } - - $tag["url"] = best_link_url($item, $sp, $tag["url"]); - - if ($tag["type"] == TERM_HASHTAG) { - $hashtags[] = "#" . $tag["term"] . ""; - $prefix = "#"; - } elseif ($tag["type"] == TERM_MENTION) { - $mentions[] = "@" . $tag["term"] . ""; - $prefix = "@"; - } - $tags[] = $prefix."" . $tag["term"] . ""; - } - dba::close($taglist); + $tags = \Friendica\Model\Term::populateTagsFromItem($item); $sp = false; $profile_link = best_link_url($item, $sp); @@ -764,9 +738,9 @@ function conversation(App $a, $items, $mode, $update, $preview = false, $order = } $body_e = $body; - $tags_e = $tags; - $hashtags_e = $hashtags; - $mentions_e = $mentions; + $tags_e = $tags['tags']; + $hashtags_e = $tags['hashtags']; + $mentions_e = $tags['mentions']; $location_e = $location; $owner_name_e = $owner_name; diff --git a/include/security.php b/include/security.php index af424df26c..b13a507cf4 100644 --- a/include/security.php +++ b/include/security.php @@ -405,12 +405,21 @@ function get_form_security_token($typename = '') function check_form_security_token($typename = '', $formname = 'form_security_token') { - if (!x($_REQUEST, $formname)) { - return false; + $hash = null; + + if (!empty($_REQUEST[$formname])) { + /// @TODO Careful, not secured! + $hash = $_REQUEST[$formname]; } - /// @TODO Careful, not secured! - $hash = $_REQUEST[$formname]; + if (!empty($_SERVER['HTTP_X_CSRF_TOKEN'])) { + /// @TODO Careful, not secured! + $hash = $_SERVER['HTTP_X_CSRF_TOKEN']; + } + + if (empty($hash)) { + return false; + } $max_livetime = 10800; // 3 hours diff --git a/include/text.php b/include/text.php index ee8a213ff6..2ec017caff 100644 --- a/include/text.php +++ b/include/text.php @@ -1234,12 +1234,6 @@ function prepare_body(array &$item, $attach = false, $is_preview = false) $a = get_app(); Addon::callHooks('prepare_body_init', $item); - $searchpath = System::baseUrl() . "/search?tag="; - - $tags = []; - $hashtags = []; - $mentions = []; - // In order to provide theme developers more possibilities, event items // are treated differently. if ($item['object-type'] === ACTIVITY_OBJ_EVENT && isset($item['event-id'])) { @@ -1247,37 +1241,11 @@ function prepare_body(array &$item, $attach = false, $is_preview = false) return $ev; } - $taglist = dba::p("SELECT `type`, `term`, `url` FROM `term` WHERE `otype` = ? AND `oid` = ? AND `type` IN (?, ?) ORDER BY `tid`", - intval(TERM_OBJ_POST), intval($item['id']), intval(TERM_HASHTAG), intval(TERM_MENTION)); + $tags = \Friendica\Model\Term::populateTagsFromItem($item); - while ($tag = dba::fetch($taglist)) { - if ($tag["url"] == "") { - $tag["url"] = $searchpath . strtolower($tag["term"]); - } - - $orig_tag = $tag["url"]; - - $tag["url"] = best_link_url($item, $sp, $tag["url"]); - - if ($tag["type"] == TERM_HASHTAG) { - if ($orig_tag != $tag["url"]) { - $item['body'] = str_replace($orig_tag, $tag["url"], $item['body']); - } - - $hashtags[] = "#" . $tag["term"] . ""; - $prefix = "#"; - } elseif ($tag["type"] == TERM_MENTION) { - $mentions[] = "@" . $tag["term"] . ""; - $prefix = "@"; - } - - $tags[] = $prefix . "" . $tag["term"] . ""; - } - dba::close($taglist); - - $item['tags'] = $tags; - $item['hashtags'] = $hashtags; - $item['mentions'] = $mentions; + $item['tags'] = $tags['tags']; + $item['hashtags'] = $tags['hashtags']; + $item['mentions'] = $tags['mentions']; // Compile eventual content filter reasons $filter_reasons = []; diff --git a/src/Core/Cache/MemcachedCacheDriver.php b/src/Core/Cache/MemcachedCacheDriver.php index d6b8d4ad58..78a4a6bdfe 100644 --- a/src/Core/Cache/MemcachedCacheDriver.php +++ b/src/Core/Cache/MemcachedCacheDriver.php @@ -50,7 +50,7 @@ class MemcachedCacheDriver extends BaseObject implements ICacheDriver { // We store with the hostname as key to avoid problems with other applications return $this->memcached->set( - self::getApp()->get_hostname() . ":" . $key, + self::getApp()->get_hostname() . ':' . $key, $value, time() + $duration ); @@ -58,7 +58,9 @@ class MemcachedCacheDriver extends BaseObject implements ICacheDriver public function delete($key) { - return $this->memcached->delete($key); + $return = $this->memcached->delete(self::getApp()->get_hostname() . ':' . $key); + + return $return; } public function clear() diff --git a/src/Core/Console.php b/src/Core/Console.php index a1143ae1d9..82c485179e 100644 --- a/src/Core/Console.php +++ b/src/Core/Console.php @@ -21,6 +21,7 @@ class Console extends \Asika\SimpleConsole\Console 'extract' => __NAMESPACE__ . '\Console\Extract', 'globalcommunityblock' => __NAMESPACE__ . '\Console\GlobalCommunityBlock', 'globalcommunitysilence' => __NAMESPACE__ . '\Console\GlobalCommunitySilence', + 'autoinstall' => __NAMESPACE__ . '\Console\AutomaticInstallation', 'maintenance' => __NAMESPACE__ . '\Console\Maintenance', 'newpassword' => __NAMESPACE__ . '\Console\NewPassword', 'php2po' => __NAMESPACE__ . '\Console\PhpToPo', @@ -42,6 +43,7 @@ Commands: globalcommunityblock Block remote profile from interacting with this node globalcommunitysilence Silence remote profile from global community page help Show help about a command, e.g (bin/console help config) + autoinstall Starts automatic installation of friendica based on values from htconfig.php maintenance Set maintenance mode for this node newpassword Set a new password for a given user php2po Generate a messages.po file from a strings.php file diff --git a/src/Core/Console/AutomaticInstallation.php b/src/Core/Console/AutomaticInstallation.php new file mode 100644 index 0000000000..6318b19a88 --- /dev/null +++ b/src/Core/Console/AutomaticInstallation.php @@ -0,0 +1,164 @@ +out("Initializing setup...\n"); + + $a = get_app(); + $db_host = ''; + $db_user = ''; + $db_pass = ''; + $db_data = ''; + require_once 'htconfig.php'; + + $this->out(" Complete!\n\n"); + + // Check basic setup + $this->out("Checking basic setup...\n"); + + $checkResults = []; + $checkResults['basic'] = $this->runBasicChecks($a); + $errorMessage = $this->extractErrors($checkResults['basic']); + + if ($errorMessage !== '') { + throw new \RuntimeException($errorMessage); + } + + $this->out(" Complete!\n\n"); + + // Check database connection + $this->out("Checking database...\n"); + + $checkResults['db'] = array(); + $checkResults['db'][] = $this->runDatabaseCheck($db_host, $db_user, $db_pass, $db_data); + $errorMessage = $this->extractErrors($checkResults['db']); + + if ($errorMessage !== '') { + throw new \RuntimeException($errorMessage); + } + + $this->out(" Complete!\n\n"); + + // Install database + $this->out("Inserting data into database...\n"); + + $checkResults['data'] = load_database(); + + if ($checkResults['data'] !== '') { + throw new \RuntimeException("ERROR: DB Database creation error. Is the DB empty?\n"); + } + + $this->out(" Complete!\n\n"); + + // Copy config file + $this->out("Saving config file...\n"); + if (!copy('htconfig.php', '.htconfig.php')) { + throw new \RuntimeException("ERROR: Saving config file failed. Please copy .htautoinstall.php to .htconfig.php manually.\n"); + } + $this->out(" Complete!\n\n"); + $this->out("\nInstallation is finished\n"); + + return 0; + } + + /** + * @param App $app + * @return array + */ + private function runBasicChecks($app) + { + $checks = []; + + check_funcs($checks); + check_imagik($checks); + check_htconfig($checks); + check_smarty3($checks); + check_keys($checks); + + if (!empty($app->config['php_path'])) { + check_php($app->config['php_path'], $checks); + } else { + throw new \RuntimeException(" ERROR: The php_path is not set in the config. Please check the file .htconfig.php.\n"); + } + + $this->out(" NOTICE: Not checking .htaccess/URL-Rewrite during CLI installation.\n"); + + return $checks; + } + + /** + * @param $db_host + * @param $db_user + * @param $db_pass + * @param $db_data + * @return array + */ + private function runDatabaseCheck($db_host, $db_user, $db_pass, $db_data) + { + $result = array( + 'title' => 'MySQL Connection', + 'required' => true, + 'status' => true, + 'help' => '', + ); + + + if (!dba::connect($db_host, $db_user, $db_pass, $db_data, true)) { + $result['status'] = false; + $result['help'] = 'Failed, please check your MySQL settings and credentials.'; + } + + return $result; + } + + /** + * @param array $results + * @return string + */ + private function extractErrors($results) + { + $errorMessage = ''; + $allChecksRequired = $this->getOption('a') !== null; + + foreach ($results as $result) { + if (($allChecksRequired || $result['required'] === true) && $result['status'] === false) { + $errorMessage .= "--------\n"; + $errorMessage .= $result['title'] . ': ' . $result['help'] . "\n"; + } + } + + return $errorMessage; + } +} diff --git a/src/Core/Session/CacheSessionHandler.php b/src/Core/Session/CacheSessionHandler.php index 463fd33d3e..507735c4fc 100644 --- a/src/Core/Session/CacheSessionHandler.php +++ b/src/Core/Session/CacheSessionHandler.php @@ -24,7 +24,7 @@ class CacheSessionHandler extends BaseObject implements SessionHandlerInterface public function read($session_id) { - if (!x($session_id)) { + if (empty($session_id)) { return ''; } @@ -58,9 +58,9 @@ class CacheSessionHandler extends BaseObject implements SessionHandlerInterface return true; } - Cache::set('session:' . $session_id, $session_data, Session::$expire); + $return = Cache::set('session:' . $session_id, $session_data, Session::$expire); - return true; + return $return; } public function close() @@ -70,8 +70,9 @@ class CacheSessionHandler extends BaseObject implements SessionHandlerInterface public function destroy($id) { - Cache::delete('session:' . $id); - return true; + $return = Cache::delete('session:' . $id); + + return $return; } public function gc($maxlifetime) diff --git a/src/Database/DBStructure.php b/src/Database/DBStructure.php index 67c8d7b8a6..bccd703720 100644 --- a/src/Database/DBStructure.php +++ b/src/Database/DBStructure.php @@ -1803,6 +1803,8 @@ class DBStructure ] ]; + \Friendica\Core\Addon::callHooks('dbstructure_definition', $database); + return $database; } } diff --git a/src/Model/Term.php b/src/Model/Term.php index d950d1d5fb..03f19197a3 100644 --- a/src/Model/Term.php +++ b/src/Model/Term.php @@ -9,6 +9,7 @@ use Friendica\Database\DBM; use dba; require_once 'boot.php'; +require_once 'include/conversation.php'; require_once 'include/dba.php'; class Term @@ -168,4 +169,56 @@ class Term } } } + + /** + * Sorts an item's tags into mentions, hashtags and other tags. Generate personalized URLs by user and modify the + * provided item's body with them. + * + * @param array $item + * @return array + */ + public static function populateTagsFromItem(&$item) + { + $return = [ + 'tags' => [], + 'hashtags' => [], + 'mentions' => [], + ]; + + $searchpath = System::baseUrl() . "/search?tag="; + + $taglist = dba::select( + 'term', + ['type', 'term', 'url'], + ["`otype` = ? AND `oid` = ? AND `type` IN (?, ?)", TERM_OBJ_POST, $item['id'], TERM_HASHTAG, TERM_MENTION], + ['order' => ['tid']] + ); + + while ($tag = dba::fetch($taglist)) { + if ($tag["url"] == "") { + $tag["url"] = $searchpath . strtolower($tag["term"]); + } + + $orig_tag = $tag["url"]; + + $tag["url"] = best_link_url($item, $sp, $tag["url"]); + + if ($tag["type"] == TERM_HASHTAG) { + if ($orig_tag != $tag["url"]) { + $item['body'] = str_replace($orig_tag, $tag["url"], $item['body']); + } + + $return['hashtags'][] = "#" . $tag["term"] . ""; + $prefix = "#"; + } elseif ($tag["type"] == TERM_MENTION) { + $return['mentions'][] = "@" . $tag["term"] . ""; + $prefix = "@"; + } + + $return['tags'][] = $prefix . "" . $tag["term"] . ""; + } + dba::close($taglist); + + return $return; + } } diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index 5d69b13d75..8120fea8a5 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -1528,36 +1528,29 @@ class Diaspora */ private static function plink($addr, $guid, $parent_guid = '') { - $r = q("SELECT `url`, `nick`, `network` FROM `fcontact` WHERE `addr`='%s' LIMIT 1", dbesc($addr)); + $contact = Contact::getDetailsByAddr($addr); // Fallback - if (!DBM::is_result($r)) { + if (!$contact) { if ($parent_guid != '') { - return "https://".substr($addr, strpos($addr, "@") + 1) . "/posts/" . $parent_guid . "#" . $guid; + return "https://" . substr($addr, strpos($addr, "@") + 1) . "/posts/" . $parent_guid . "#" . $guid; } else { - return "https://".substr($addr, strpos($addr, "@") + 1) . "/posts/" . $guid; + return "https://" . substr($addr, strpos($addr, "@") + 1) . "/posts/" . $guid; } } - // Friendica contacts are often detected as Diaspora contacts in the "fcontact" table - // So we try another way as well. - $s = q("SELECT `network` FROM `gcontact` WHERE `nurl`='%s' LIMIT 1", dbesc(normalise_link($r[0]["url"]))); - if (DBM::is_result($s)) { - $r[0]["network"] = $s[0]["network"]; + if ($contact["network"] == NETWORK_DFRN) { + return str_replace("/profile/" . $contact["nick"] . "/", "/display/" . $guid, $contact["url"] . "/"); } - if ($r[0]["network"] == NETWORK_DFRN) { - return str_replace("/profile/".$r[0]["nick"]."/", "/display/".$guid, $r[0]["url"]."/"); - } - - if (self::isRedmatrix($r[0]["url"])) { - return $r[0]["url"]."/?f=&mid=".$guid; + if (self::isRedmatrix($contact["url"])) { + return $contact["url"] . "/?f=&mid=" . $guid; } if ($parent_guid != '') { - return "https://".substr($addr, strpos($addr, "@") + 1) . "/posts/" . $parent_guid . "#" . $guid; + return "https://" . substr($addr, strpos($addr, "@") + 1) . "/posts/" . $parent_guid . "#" . $guid; } else { - return "https://".substr($addr, strpos($addr, "@") + 1) . "/posts/" . $guid; + return "https://" . substr($addr, strpos($addr, "@") + 1) . "/posts/" . $guid; } } @@ -2744,35 +2737,33 @@ class Diaspora * * @return array The fetched item */ - private static function originalItem($guid, $orig_author, $author) + public static function originalItem($guid, $orig_author) { // Do we already have this item? - $r = q( - "SELECT `body`, `tag`, `app`, `created`, `object-type`, `uri`, `guid`, - `author-name`, `author-link`, `author-avatar` - FROM `item` WHERE `guid` = '%s' AND `visible` AND NOT `deleted` AND `body` != '' LIMIT 1", - dbesc($guid) - ); + $fields = ['body', 'tag', 'app', 'created', 'object-type', 'uri', 'guid', + 'author-name', 'author-link', 'author-avatar']; + $condition = ['guid' => $guid, 'visible' => true, 'deleted' => false]; + $item = dba::selectfirst('item', $fields, $condition); - if (DBM::is_result($r)) { + if (DBM::is_result($item)) { logger("reshared message ".$guid." already exists on system."); // Maybe it is already a reshared item? // Then refetch the content, if it is a reshare from a reshare. // If it is a reshared post from another network then reformat to avoid display problems with two share elements - if (self::isReshare($r[0]["body"], true)) { + if (self::isReshare($item["body"], true)) { $r = []; - } elseif (self::isReshare($r[0]["body"], false) || strstr($r[0]["body"], "[share")) { - $r[0]["body"] = Markdown::toBBCode(BBCode::toMarkdown($r[0]["body"])); + } elseif (self::isReshare($item["body"], false) || strstr($item["body"], "[share")) { + $item["body"] = Markdown::toBBCode(BBCode::toMarkdown($item["body"])); - $r[0]["body"] = self::replacePeopleGuid($r[0]["body"], $r[0]["author-link"]); + $item["body"] = self::replacePeopleGuid($item["body"], $item["author-link"]); // Add OEmbed and other information to the body - $r[0]["body"] = add_page_info_to_body($r[0]["body"], false, true); + $item["body"] = add_page_info_to_body($item["body"], false, true); - return $r[0]; + return $item; } else { - return $r[0]; + return $item; } } @@ -2788,21 +2779,19 @@ class Diaspora } if ($item_id) { - $r = q( - "SELECT `body`, `tag`, `app`, `created`, `object-type`, `uri`, `guid`, - `author-name`, `author-link`, `author-avatar` - FROM `item` WHERE `id` = %d AND `visible` AND NOT `deleted` AND `body` != '' LIMIT 1", - intval($item_id) - ); + $fields = ['body', 'tag', 'app', 'created', 'object-type', 'uri', 'guid', + 'author-name', 'author-link', 'author-avatar']; + $condition = ['id' => $item_id, 'visible' => true, 'deleted' => false]; + $item = dba::selectfirst('item', $fields, $condition); - if (DBM::is_result($r)) { + if (DBM::is_result($item)) { // If it is a reshared post from another network then reformat to avoid display problems with two share elements - if (self::isReshare($r[0]["body"], false)) { - $r[0]["body"] = Markdown::toBBCode(BBCode::toMarkdown($r[0]["body"])); - $r[0]["body"] = self::replacePeopleGuid($r[0]["body"], $r[0]["author-link"]); + if (self::isReshare($item["body"], false)) { + $item["body"] = Markdown::toBBCode(BBCode::toMarkdown($item["body"])); + $item["body"] = self::replacePeopleGuid($item["body"], $item["author-link"]); } - return $r[0]; + return $item; } } } @@ -2838,7 +2827,7 @@ class Diaspora return true; } - $original_item = self::originalItem($root_guid, $root_author, $author); + $original_item = self::originalItem($root_guid, $root_author); if (!$original_item) { return false; } @@ -3556,24 +3545,21 @@ class Diaspora // Skip if it isn't a pure repeated messages // Does it start with a share? if ((strpos($body, "[share") > 0) && $complete) { - return(false); + return false; } // Does it end with a share? if (strlen($body) > (strrpos($body, "[/share]") + 8)) { - return(false); + return false; } $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism", "$1", $body); // Skip if there is no shared message in there if ($body == $attributes) { - return(false); + return false; } // If we don't do the complete check we quit here - if (!$complete) { - return true; - } $guid = ""; preg_match("/guid='(.*?)'/ism", $attributes, $matches); @@ -3586,18 +3572,14 @@ class Diaspora $guid = $matches[1]; } - if ($guid != "") { - $r = q( - "SELECT `contact-id` FROM `item` WHERE `guid` = '%s' AND `network` IN ('%s', '%s') LIMIT 1", - dbesc($guid), - NETWORK_DFRN, - NETWORK_DIASPORA - ); - if ($r) { + if (($guid != "") && $complete) { + $condition = ['guid' => $guid, 'network' => [NETWORK_DFRN, NETWORK_DIASPORA]]; + $item = dba::selectFirst('item', ['contact-id'], $condition); + if (DBM::is_result($item)) { $ret= []; - $ret["root_handle"] = self::handleFromContact($r[0]["contact-id"]); + $ret["root_handle"] = self::handleFromContact($item["contact-id"]); $ret["root_guid"] = $guid; - return($ret); + return $ret; } } @@ -3614,28 +3596,22 @@ class Diaspora $ret= []; - $ret["root_handle"] = preg_replace("=https?://(.*)/u/(.*)=ism", "$2@$1", $profile); - if (($ret["root_handle"] == $profile) || ($ret["root_handle"] == "")) { - return(false); + if ($profile != "") { + if (Contact::getIdForURL($profile)) { + $author = Contact::getDetailsByURL($profile); + $ret["root_handle"] = $author['addr']; + } } - $link = ""; - preg_match("/link='(.*?)'/ism", $attributes, $matches); - if ($matches[1] != "") { - $link = $matches[1]; + if (!empty($guid)) { + $ret["root_guid"] = $guid; } - preg_match('/link="(.*?)"/ism', $attributes, $matches); - if ($matches[1] != "") { - $link = $matches[1]; + if (empty($ret) && !$complete) { + return true; } - $ret["root_guid"] = preg_replace("=https?://(.*)/posts/(.*)=ism", "$2", $link); - if (($ret["root_guid"] == $link) || (trim($ret["root_guid"]) == "")) { - return(false); - } - - return($ret); + return $ret; } /** diff --git a/view/theme/frio/css/mod_admin.css b/view/theme/frio/css/mod_admin.css new file mode 100644 index 0000000000..7a8c6f659e --- /dev/null +++ b/view/theme/frio/css/mod_admin.css @@ -0,0 +1,8 @@ + +#admin-users.adminpage { padding-left:0; padding-right: 0;} +#admin-users.adminpage > h1 { padding: 0 15px; } +#users img.icon, #deleted img.icon { height: 24px; } +.opened .caret { transform: rotate(180deg); } +tr.details td, +tr.details th +{ border-top: 0!important; } diff --git a/view/theme/frio/js/mod_admin.js b/view/theme/frio/js/mod_admin.js index b9fc467813..dc8abe054b 100644 --- a/view/theme/frio/js/mod_admin.js +++ b/view/theme/frio/js/mod_admin.js @@ -20,6 +20,7 @@ $(function() { } }); + function selectall(cls) { $('.' + cls).prop('checked', true); return false; @@ -28,4 +29,17 @@ $(function() { $('.' + cls).prop('checked', false); return false; } + + }); + +// Users +function confirm_delete(msg, uname){ + return confirm(msg.format(uname)); +} + +function details(uid) { + $("#user-"+uid+"-detail").toggleClass("hidden"); + $("#user-"+uid).toggleClass("opened"); + return false; +} diff --git a/view/theme/frio/templates/admin/users.tpl b/view/theme/frio/templates/admin/users.tpl new file mode 100644 index 0000000000..831ee49b6a --- /dev/null +++ b/view/theme/frio/templates/admin/users.tpl @@ -0,0 +1,266 @@ + + + +
+ {{foreach $th_deleted as $k=>$th}} + {{if in_array($k,[0,1,5])}} + | {{$th}} | + {{/if}} + {{/foreach}} +||
---|---|---|---|
+ | {{$u.name}} | +{{$u.email}} | +{{$u.deleted}} | +