Merge pull request #54 from nextcloud/develop-14

Merge develop-14 into develop
This commit is contained in:
Marcin Łojewski
2018-07-09 20:24:41 +02:00
committed by GitHub
5 changed files with 135 additions and 106 deletions

View File

@@ -16,10 +16,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Example SQL script in README file - Example SQL script in README file
- Fixed misspelling - Fixed misspelling
### Changed
- Support for Nextcloud 14 only
- Group backend implementation
- User backend implementation
### Fixed ### Fixed
- Table and column autocomplete in settings panel - Table and column autocomplete in settings panel
## [v4.0.0-rc2] - 2018-06-14 ## [4.0.0-rc2] - 2018-06-14
### Added ### Added
- User active column - User active column
@@ -79,6 +84,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Supported version of ownCloud, Nextcloud: ownCloud 10, Nextcloud 12 - Supported version of ownCloud, Nextcloud: ownCloud 10, Nextcloud 12
[Unreleased]: https://github.com/nextcloud/user_sql/compare/v4.0.0-rc2...develop [Unreleased]: https://github.com/nextcloud/user_sql/compare/v4.0.0-rc2...develop
[v4.0.0-rc2]: https://github.com/nextcloud/user_sql/compare/v4.0.0-rc1...v4.0.0-rc2 [4.0.0-rc2]: https://github.com/nextcloud/user_sql/compare/v4.0.0-rc1...v4.0.0-rc2
[4.0.0-rc1]: https://github.com/nextcloud/user_sql/compare/v3.1.0...v4.0.0-rc1 [4.0.0-rc1]: https://github.com/nextcloud/user_sql/compare/v3.1.0...v4.0.0-rc1
[3.1.0]: https://github.com/nextcloud/user_sql/compare/v2.4.0...v3.1.0 [3.1.0]: https://github.com/nextcloud/user_sql/compare/v2.4.0...v3.1.0

View File

@@ -10,8 +10,8 @@
</description> </description>
<version>4.0.0-dev</version> <version>4.0.0-dev</version>
<licence>agpl</licence> <licence>agpl</licence>
<author>Andreas Böhler &lt;dev (at) aboehler (dot) at&gt;</author> <author>Marcin Łojewski</author>
<author>Marcin Łojewski &lt;dev@mlojewski.me&gt;</author> <author>Andreas Böhler</author>
<namespace>UserSQL</namespace> <namespace>UserSQL</namespace>
<bugs>https://github.com/nextcloud/user_sql/issues</bugs> <bugs>https://github.com/nextcloud/user_sql/issues</bugs>
<repository>https://github.com/nextcloud/user_sql</repository> <repository>https://github.com/nextcloud/user_sql</repository>
@@ -22,7 +22,7 @@
<category>auth</category> <category>auth</category>
<dependencies> <dependencies>
<php min-version="7.0"/> <php min-version="7.0"/>
<nextcloud min-version="13" max-version="13"/> <nextcloud min-version="14" max-version="14"/>
</dependencies> </dependencies>
<settings> <settings>
<admin>\OCA\UserSQL\Settings\Admin</admin> <admin>\OCA\UserSQL\Settings\Admin</admin>

View File

@@ -21,12 +21,15 @@
namespace OCA\UserSQL\Backend; namespace OCA\UserSQL\Backend;
use OC\Group\Backend;
use OCA\UserSQL\Cache; use OCA\UserSQL\Cache;
use OCA\UserSQL\Constant\DB; use OCA\UserSQL\Constant\DB;
use OCA\UserSQL\Model\Group; use OCA\UserSQL\Model\Group;
use OCA\UserSQL\Properties; use OCA\UserSQL\Properties;
use OCA\UserSQL\Repository\GroupRepository; use OCA\UserSQL\Repository\GroupRepository;
use OCP\Group\Backend\ABackend;
use OCP\Group\Backend\ICountUsersBackend;
use OCP\Group\Backend\IGroupDetailsBackend;
use OCP\Group\Backend\IIsAdminBackend;
use OCP\ILogger; use OCP\ILogger;
/** /**
@@ -34,7 +37,10 @@ use OCP\ILogger;
* *
* @author Marcin Łojewski <dev@mlojewski.me> * @author Marcin Łojewski <dev@mlojewski.me>
*/ */
final class GroupBackend extends Backend final class GroupBackend extends ABackend implements
ICountUsersBackend,
IGroupDetailsBackend,
IIsAdminBackend
{ {
/** /**
* @var string The application name. * @var string The application name.
@@ -128,14 +134,9 @@ final class GroupBackend extends Backend
} }
/** /**
* Returns the number of users in given group matching the search term. * @inheritdoc
*
* @param string $gid The group ID.
* @param string $search The search term.
*
* @return int The number of users in given group matching the search term.
*/ */
public function countUsersInGroup($gid, $search = "") public function countUsersInGroup(string $gid, string $search = ""): int
{ {
$this->logger->debug( $this->logger->debug(
"Entering countUsersInGroup($gid, $search)", "Entering countUsersInGroup($gid, $search)",
@@ -355,18 +356,18 @@ final class GroupBackend extends Backend
} }
/** /**
* Checks if a user is in the admin group. * @inheritdoc
*
* @param string $uid User ID.
*
* @return bool TRUE if a user is in the admin group, FALSE otherwise.
*/ */
public function isAdmin($uid) public function isAdmin(string $uid = null): bool
{ {
$this->logger->debug( $this->logger->debug(
"Entering isAdmin($uid)", ["app" => $this->appName] "Entering isAdmin($uid)", ["app" => $this->appName]
); );
if (empty($this->properties[DB::GROUP_ADMIN_COLUMN]) || $uid === null) {
return false;
}
$cacheKey = self::class . "admin_" . $uid; $cacheKey = self::class . "admin_" . $uid;
$admin = $this->cache->get($cacheKey); $admin = $this->cache->get($cacheKey);
@@ -394,18 +395,18 @@ final class GroupBackend extends Backend
} }
/** /**
* Get associative array of the group details. * @inheritdoc
*
* @param string $gid The group ID.
*
* @return array Associative array of the group details.
*/ */
public function getGroupDetails($gid) public function getGroupDetails(string $gid): array
{ {
$this->logger->debug( $this->logger->debug(
"Entering getGroupDetails($gid)", ["app" => $this->appName] "Entering getGroupDetails($gid)", ["app" => $this->appName]
); );
if (empty($this->properties[DB::GROUP_NAME_COLUMN])) {
return [];
}
$group = $this->getGroup($gid); $group = $this->getGroup($gid);
if (!($group instanceof Group)) { if (!($group instanceof Group)) {
@@ -421,21 +422,6 @@ final class GroupBackend extends Backend
return $details; return $details;
} }
/**
* @inheritdoc
*/
public function getSupportedActions()
{
$actions = parent::getSupportedActions();
$actions &= empty($this->properties[DB::GROUP_ADMIN_COLUMN])
? ~Backend::IS_ADMIN : ~0;
$actions &= empty($this->properties[DB::GROUP_NAME_COLUMN])
? ~Backend::GROUP_DETAILS : ~0;
return $actions;
}
/** /**
* Check if this backend is correctly set and can be enabled. * Check if this backend is correctly set and can be enabled.
* *

View File

@@ -21,7 +21,6 @@
namespace OCA\UserSQL\Backend; namespace OCA\UserSQL\Backend;
use OC\User\Backend;
use OCA\UserSQL\Action\EmailSync; use OCA\UserSQL\Action\EmailSync;
use OCA\UserSQL\Action\IUserAction; use OCA\UserSQL\Action\IUserAction;
use OCA\UserSQL\Action\QuotaSync; use OCA\UserSQL\Action\QuotaSync;
@@ -36,13 +35,28 @@ use OCA\UserSQL\Repository\UserRepository;
use OCP\IConfig; use OCP\IConfig;
use OCP\IL10N; use OCP\IL10N;
use OCP\ILogger; use OCP\ILogger;
use OCP\User\Backend\ABackend;
use OCP\User\Backend\ICheckPasswordBackend;
use OCP\User\Backend\ICountUsersBackend;
use OCP\User\Backend\IGetDisplayNameBackend;
use OCP\User\Backend\IGetHomeBackend;
use OCP\User\Backend\IProvideAvatarBackend;
use OCP\User\Backend\ISetDisplayNameBackend;
use OCP\User\Backend\ISetPasswordBackend;
/** /**
* The SQL user backend manager. * The SQL user backend manager.
* *
* @author Marcin Łojewski <dev@mlojewski.me> * @author Marcin Łojewski <dev@mlojewski.me>
*/ */
final class UserBackend extends Backend final class UserBackend extends ABackend implements
ICheckPasswordBackend,
ICountUsersBackend,
IGetDisplayNameBackend,
IGetHomeBackend,
IProvideAvatarBackend,
ISetDisplayNameBackend,
ISetPasswordBackend
{ {
/** /**
* @var string The application name. * @var string The application name.
@@ -237,7 +251,7 @@ final class UserBackend extends Backend
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getDisplayName($uid) public function getDisplayName($uid): string
{ {
$this->logger->debug( $this->logger->debug(
"Entering getDisplayName($uid)", ["app" => $this->appName] "Entering getDisplayName($uid)", ["app" => $this->appName]
@@ -267,7 +281,7 @@ final class UserBackend extends Backend
* *
* @return string|bool The user ID on success, false otherwise. * @return string|bool The user ID on success, false otherwise.
*/ */
public function checkPassword($uid, $password) public function checkPassword(string $uid, string $password)
{ {
$this->logger->debug( $this->logger->debug(
"Entering checkPassword($uid, *)", ["app" => $this->appName] "Entering checkPassword($uid, *)", ["app" => $this->appName]
@@ -346,6 +360,10 @@ final class UserBackend extends Backend
["app" => $this->appName] ["app" => $this->appName]
); );
if (empty($this->properties[DB::USER_NAME_COLUMN])) {
return false;
}
$users = $this->getUsers($search, $limit, $offset); $users = $this->getUsers($search, $limit, $offset);
$names = []; $names = [];
@@ -419,12 +437,16 @@ final class UserBackend extends Backend
* *
* @return bool TRUE if the password has been set, FALSE otherwise. * @return bool TRUE if the password has been set, FALSE otherwise.
*/ */
public function setPassword($uid, $password) public function setPassword(string $uid, string $password): bool
{ {
$this->logger->debug( $this->logger->debug(
"Entering setPassword($uid, *)", ["app" => "user_sql"] "Entering setPassword($uid, *)", ["app" => "user_sql"]
); );
if (empty($this->properties[Opt::PASSWORD_CHANGE])) {
return false;
}
$passwordAlgorithm = $this->getPasswordAlgorithm(); $passwordAlgorithm = $this->getPasswordAlgorithm();
if ($passwordAlgorithm === false) { if ($passwordAlgorithm === false) {
return false; return false;
@@ -461,12 +483,16 @@ final class UserBackend extends Backend
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function getHome($uid) public function getHome(string $uid)
{ {
$this->logger->debug( $this->logger->debug(
"Entering getHome($uid)", ["app" => $this->appName] "Entering getHome($uid)", ["app" => $this->appName]
); );
if (empty($this->properties[Opt::HOME_MODE])) {
return false;
}
$home = false; $home = false;
switch ($this->properties[Opt::HOME_MODE]) { switch ($this->properties[Opt::HOME_MODE]) {
case App::HOME_STATIC: case App::HOME_STATIC:
@@ -496,12 +522,16 @@ final class UserBackend extends Backend
* *
* @return bool TRUE if the user can change its avatar, FALSE otherwise. * @return bool TRUE if the user can change its avatar, FALSE otherwise.
*/ */
public function canChangeAvatar($uid) public function canChangeAvatar(string $uid): bool
{ {
$this->logger->debug( $this->logger->debug(
"Entering canChangeAvatar($uid)", ["app" => $this->appName] "Entering canChangeAvatar($uid)", ["app" => $this->appName]
); );
if (empty($this->properties[DB::USER_AVATAR_COLUMN])) {
return false;
}
$user = $this->userRepository->findByUid($uid); $user = $this->userRepository->findByUid($uid);
if (!($user instanceof User)) { if (!($user instanceof User)) {
return false; return false;
@@ -524,13 +554,17 @@ final class UserBackend extends Backend
* *
* @return bool TRUE if the password has been set, FALSE otherwise. * @return bool TRUE if the password has been set, FALSE otherwise.
*/ */
public function setDisplayName($uid, $displayName) public function setDisplayName(string $uid, string $displayName): bool
{ {
$this->logger->debug( $this->logger->debug(
"Entering setDisplayName($uid, $displayName)", "Entering setDisplayName($uid, $displayName)",
["app" => $this->appName] ["app" => $this->appName]
); );
if (empty($this->properties[Opt::NAME_CHANGE])) {
return false;
}
$user = $this->userRepository->findByUid($uid); $user = $this->userRepository->findByUid($uid);
if (!($user instanceof User)) { if (!($user instanceof User)) {
return false; return false;
@@ -550,28 +584,6 @@ final class UserBackend extends Backend
return false; return false;
} }
/**
* @inheritdoc
*/
public function getSupportedActions()
{
$actions = parent::getSupportedActions();
$actions &= empty($this->properties[DB::USER_NAME_COLUMN])
? ~Backend::GET_DISPLAYNAME : ~0;
$actions &= empty($this->properties[Opt::HOME_MODE])
? ~Backend::GET_HOME : ~0;
$actions &= empty($this->properties[DB::USER_AVATAR_COLUMN])
? ~Backend::PROVIDE_AVATAR : ~0;
$actions &= (!empty($this->properties[DB::USER_NAME_COLUMN])
&& $this->properties[Opt::NAME_CHANGE]) ? ~0
: ~Backend::SET_DISPLAYNAME;
$actions &= $this->properties[Opt::PASSWORD_CHANGE] ? ~0
: ~Backend::SET_PASSWORD;
return $actions;
}
/** /**
* Check if this backend is correctly set and can be enabled. * Check if this backend is correctly set and can be enabled.
* *
@@ -589,4 +601,20 @@ final class UserBackend extends Backend
&& !empty($this->properties[DB::USER_PASSWORD_COLUMN]) && !empty($this->properties[DB::USER_PASSWORD_COLUMN])
&& !empty($this->properties[Opt::CRYPTO_CLASS]); && !empty($this->properties[Opt::CRYPTO_CLASS]);
} }
/**
* @inheritdoc
*/
public function getBackendName()
{
return "User SQL";
}
/**
* @inheritdoc
*/
public function deleteUser($uid)
{
return false;
}
} }

View File

@@ -21,8 +21,8 @@
use OCP\IL10N; use OCP\IL10N;
script('user_sql', 'settings'); script("user_sql", "settings");
style('user_sql', 'settings'); style("user_sql", "settings");
function print_text_input(IL10N $l, $id, $label, $value = "", $type = "text") function print_text_input(IL10N $l, $id, $label, $value = "", $type = "text")
{ {
@@ -71,6 +71,16 @@ function print_select_options(
echo "</option>"; echo "</option>";
} }
#54: Merge develop-14 into develop Conflicts
Resolved all conflicts
2 conflicting files
CHANGELOG.md
CHANGELOG.md
admin.php
templates/admin.php
templates/admin.php
Resolved
echo "</select>"; echo "</select>";
echo "</label></div>"; echo "</label></div>";
} }
@@ -94,11 +104,11 @@ function print_select_options(
<p class="settings-hint"><?php p($l->t("Define your database connection parameters.")); ?></p> <p class="settings-hint"><?php p($l->t("Define your database connection parameters.")); ?></p>
<fieldset><?php <fieldset><?php
$drivers = ["mysql" => "MySQL", "pgsql" => "PostgreSQL"]; $drivers = ["mysql" => "MySQL", "pgsql" => "PostgreSQL"];
print_select_options($l, "db-driver", "SQL driver", $drivers, $_['db.driver']); print_select_options($l, "db-driver", "SQL driver", $drivers, $_["db.driver"]);
print_text_input($l, "db-hostname", "Hostname", $_['db.hostname']); print_text_input($l, "db-hostname", "Hostname", $_["db.hostname"]);
print_text_input($l, "db-database", "Database", $_['db.database']); print_text_input($l, "db-database", "Database", $_["db.database"]);
print_text_input($l, "db-username", "Username", $_['db.username']); print_text_input($l, "db-username", "Username", $_["db.username"]);
print_text_input($l, "db-password", "Password", $_['db.password'], "password"); ?> print_text_input($l, "db-password", "Password", $_["db.password"], "password"); ?>
<div class="button-right"> <div class="button-right">
<input type="submit" id="user_sql-db_connection_verify" value="<?php p($l->t("Verify settings")); ?>"> <input type="submit" id="user_sql-db_connection_verify" value="<?php p($l->t("Verify settings")); ?>">
</div> </div>
@@ -108,16 +118,16 @@ function print_select_options(
<h2><?php p($l->t("Options")); ?></h2> <h2><?php p($l->t("Options")); ?></h2>
<p class="settings-hint"><?php p($l->t("Here are all currently supported options.")); ?></p> <p class="settings-hint"><?php p($l->t("Here are all currently supported options.")); ?></p>
<fieldset><?php <fieldset><?php
print_checkbox_input($l, "opt-name_change", "Allow display name change", $_['opt.name_change']); print_checkbox_input($l, "opt-name_change", "Allow display name change", $_["opt.name_change"]);
print_checkbox_input($l, "opt-password_change", "Allow password change", $_['opt.password_change']); ?> print_checkbox_input($l, "opt-password_change", "Allow password change", $_["opt.password_change"]); ?>
<div class="button-right"><?php <div class="button-right"><?php
print_checkbox_input($l, "opt-use_cache", "Use cache", $_['opt.use_cache'], false); ?> print_checkbox_input($l, "opt-use_cache", "Use cache", $_["opt.use_cache"], false); ?>
<input type="submit" id="user_sql-clear_cache" value="<?php p($l->t("Clear cache")); ?>"> <input type="submit" id="user_sql-clear_cache" value="<?php p($l->t("Clear cache")); ?>">
</div> </div>
<?php <?php
$hashes = []; $hashes = [];
foreach (glob(__DIR__ . "/../lib/Crypto/*.php") as $filename) { foreach (glob(__DIR__ . "/../lib/Crypto/*.php") as $filename) {
$class = 'OCA\\UserSQL\\Crypto\\' . basename(substr($filename, 0, -4)); $class = "OCA\\UserSQL\\Crypto\\" . basename(substr($filename, 0, -4));
try { try {
$passwordAlgorithm = new $class($l); $passwordAlgorithm = new $class($l);
if ($passwordAlgorithm instanceof if ($passwordAlgorithm instanceof
@@ -129,58 +139,58 @@ function print_select_options(
} }
} }
print_select_options($l, "opt-crypto_class", "Hash algorithm", $hashes, $_['opt.crypto_class']); print_select_options($l, "opt-crypto_class", "Hash algorithm", $hashes, $_["opt.crypto_class"]);
print_select_options($l, "opt-email_sync", "Email sync", ["" => "None", "initial" => "Synchronise only once", "force_nc"=>"Nextcloud always wins", "force_sql"=>"SQL always wins"], $_['opt.email_sync']); print_select_options($l, "opt-email_sync", "Email sync", ["" => "None", "initial" => "Synchronise only once", "force_nc"=>"Nextcloud always wins", "force_sql"=>"SQL always wins"], $_["opt.email_sync"]);
print_select_options($l, "opt-quota_sync", "Quota sync", ["" => "None", "initial" => "Synchronise only once", "force_nc"=>"Nextcloud always wins", "force_sql"=>"SQL always wins"], $_['opt.quota_sync']); print_select_options($l, "opt-quota_sync", "Quota sync", ["" => "None", "initial" => "Synchronise only once", "force_nc"=>"Nextcloud always wins", "force_sql"=>"SQL always wins"], $_["opt.quota_sync"]);
print_select_options($l, "opt-home_mode", "Home mode", ["" => "Default", "query" => "Query", "static" => "Static"], $_['opt.home_mode']); print_select_options($l, "opt-home_mode", "Home mode", ["" => "Default", "query" => "Query", "static" => "Static"], $_["opt.home_mode"]);
print_text_input($l, "opt-home_location", "Home Location", $_['opt.home_location']); ?> print_text_input($l, "opt-home_location", "Home Location", $_["opt.home_location"]); ?>
</fieldset> </fieldset>
</div> </div>
<div class="section clear-left"> <div class="section clear-left">
<h2><?php p($l->t("User table")); ?></h2> <h2><?php p($l->t("User table")); ?></h2>
<p class="settings-hint"><?php p($l->t("Table containing user accounts.")); ?></p> <p class="settings-hint"><?php p($l->t("Table containing user accounts.")); ?></p>
<fieldset><?php <fieldset><?php
print_text_input($l, "db-table-user", "Table name", $_['db.table.user']) ;?> print_text_input($l, "db-table-user", "Table name", $_["db.table.user"]); ?>
<h3><?php p($l->t("Columns")); ?></h3> <h3><?php p($l->t("Columns")); ?></h3>
<?php <?php
print_text_input($l, "db-table-user-column-uid", "Username", $_['db.table.user.column.uid']); print_text_input($l, "db-table-user-column-uid", "Username", $_["db.table.user.column.uid"]);
print_text_input($l, "db-table-user-column-email", "Email", $_['db.table.user.column.email']); print_text_input($l, "db-table-user-column-email", "Email", $_["db.table.user.column.email"]);
print_text_input($l, "db-table-user-column-quota", "Quota", $_['db.table.user.column.quota']); print_text_input($l, "db-table-user-column-quota", "Quota", $_["db.table.user.column.quota"]);
print_text_input($l, "db-table-user-column-home", "Home", $_['db.table.user.column.home']); print_text_input($l, "db-table-user-column-home", "Home", $_["db.table.user.column.home"]);
print_text_input($l, "db-table-user-column-password", "Password", $_['db.table.user.column.password']); print_text_input($l, "db-table-user-column-password", "Password", $_["db.table.user.column.password"]);
print_text_input($l, "db-table-user-column-name", "Display name", $_['db.table.user.column.name']); print_text_input($l, "db-table-user-column-name", "Display name", $_["db.table.user.column.name"]);
print_text_input($l, "db-table-user-column-active", "Active", $_['db.table.user.column.active']); print_text_input($l, "db-table-user-column-active", "Active", $_["db.table.user.column.active"]);
print_text_input($l, "db-table-user-column-avatar", "Provide avatar", $_['db.table.user.column.avatar']); print_text_input($l, "db-table-user-column-avatar", "Provide avatar", $_["db.table.user.column.avatar"]);
print_text_input($l, "db-table-user-column-salt", "Salt", $_['db.table.user.column.salt']); ?> print_text_input($l, "db-table-user-column-salt", "Salt", $_["db.table.user.column.salt"]); ?>
</fieldset> </fieldset>
</div> </div>
<div class="section"> <div class="section">
<h2><?php p($l->t("Group table")); ?></h2> <h2><?php p($l->t("Group table")); ?></h2>
<p class="settings-hint"><?php p($l->t("Group definitions table.")); ?></p> <p class="settings-hint"><?php p($l->t("Group definitions table.")); ?></p>
<fieldset><?php <fieldset><?php
print_text_input($l, "db-table-group", "Table name", $_['db.table.group']); ?> print_text_input($l, "db-table-group", "Table name", $_["db.table.group"]); ?>
<h3><?php p($l->t("Columns")); ?></h3> <h3><?php p($l->t("Columns")); ?></h3>
<?php <?php
print_text_input($l, "db-table-group-column-admin", "Is admin", $_['db.table.group.column.admin']); print_text_input($l, "db-table-group-column-admin", "Is admin", $_["db.table.group.column.admin"]);
print_text_input($l, "db-table-group-column-name", "Display name", $_['db.table.group.column.name']); print_text_input($l, "db-table-group-column-name", "Display name", $_["db.table.group.column.name"]);
print_text_input($l, "db-table-group-column-gid", "Group name", $_['db.table.group.column.gid']); ?> print_text_input($l, "db-table-group-column-gid", "Group name", $_["db.table.group.column.gid"]); ?>
</fieldset> </fieldset>
</div> </div>
<div class="section"> <div class="section">
<h2><?php p($l->t("User group table")); ?></h2> <h2><?php p($l->t("User group table")); ?></h2>
<p class="settings-hint"><?php p($l->t("Associative table which maps users to groups.")); ?></p> <p class="settings-hint"><?php p($l->t("Associative table which maps users to groups.")); ?></p>
<fieldset><?php <fieldset><?php
print_text_input($l, "db-table-user_group", "Table name", $_['db.table.user_group']); ?> print_text_input($l, "db-table-user_group", "Table name", $_["db.table.user_group"]); ?>
<h3><?php p($l->t("Columns")); ?></h3> <h3><?php p($l->t("Columns")); ?></h3>
<?php <?php
print_text_input($l, "db-table-user_group-column-uid", "Username", $_['db.table.user_group.column.uid']); print_text_input($l, "db-table-user_group-column-uid", "Username", $_["db.table.user_group.column.uid"]);
print_text_input($l, "db-table-user_group-column-gid", "Group name", $_['db.table.user_group.column.gid']); ?> print_text_input($l, "db-table-user_group-column-gid", "Group name", $_["db.table.user_group.column.gid"]); ?>
</fieldset> </fieldset>
</div> </div>
</div> </div>
<div class="section"> <div class="section">
<input type="hidden" name="appname" value="user_sql"/> <input type="hidden" name="appname" value="user_sql"/>
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']); ?>" id="requesttoken"/> <input type="hidden" name="requesttoken" value="<?php p($_["requesttoken"]); ?>" id="requesttoken"/>
<input id="user_sql-save" type="submit" value="<?php p($l->t('Save')); ?>"/> <input id="user_sql-save" type="submit" value="<?php p($l->t("Save")); ?>"/>
</div> </div>
</form> </form>