mirror of https://github.com/calzoneman/sync.git
Work on account channels interface; Start work on /account/profile
This commit is contained in:
parent
5fe5dd440e
commit
ead38a9d35
|
@ -81,20 +81,20 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof name !== "string" || typeof pw !== "string") {
|
if (typeof name !== "string" || typeof pw !== "string") {
|
||||||
callback(new Error("You must provide a nonempty username and password"), null);
|
callback("You must provide a nonempty username and password", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var lname = name.toLowerCase();
|
var lname = name.toLowerCase();
|
||||||
|
|
||||||
if (registrationLock[lname]) {
|
if (registrationLock[lname]) {
|
||||||
callback(new Error("There is already a registration in progress for "+name),
|
callback("There is already a registration in progress for "+name,
|
||||||
null);
|
null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$util.isValidUserName(name)) {
|
if (!$util.isValidUserName(name)) {
|
||||||
callback(new Error("Invalid username. Usernames may consist of 1-20 " +
|
callback("Invalid username. Usernames may consist of 1-20 " +
|
||||||
"characters a-z, A-Z, 0-9, -, _, and accented letters."),
|
"characters a-z, A-Z, 0-9, -, _, and accented letters.",
|
||||||
null);
|
null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ module.exports = {
|
||||||
|
|
||||||
if (taken) {
|
if (taken) {
|
||||||
delete registrationLock[lname];
|
delete registrationLock[lname];
|
||||||
callback(new Error("Username is already registered"), null);
|
callback("Username is already registered", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof name !== "string" || typeof pw !== "string") {
|
if (typeof name !== "string" || typeof pw !== "string") {
|
||||||
callback(new Error("Invalid username/password combination"), null);
|
callback("Invalid username/password combination", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rows.length === 0) {
|
if (rows.length === 0) {
|
||||||
callback(new Error("User does not exist"), null);
|
callback("User does not exist", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ module.exports = {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err, null);
|
callback(err, null);
|
||||||
} else if (!match) {
|
} else if (!match) {
|
||||||
callback(new Error("Invalid username/password combination"), null);
|
callback("Invalid username/password combination", null);
|
||||||
} else {
|
} else {
|
||||||
callback(null, {
|
callback(null, {
|
||||||
name: rows[0].name,
|
name: rows[0].name,
|
||||||
|
@ -214,13 +214,13 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof auth !== "string") {
|
if (typeof auth !== "string") {
|
||||||
callback(new Error("Invalid auth string"), null);
|
callback("Invalid auth string", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var split = auth.split(":");
|
var split = auth.split(":");
|
||||||
if (split.length !== 2) {
|
if (split.length !== 2) {
|
||||||
callback(new Error("Invalid auth string"), null);
|
callback("Invalid auth string", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +235,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rows.length === 0) {
|
if (rows.length === 0) {
|
||||||
callback(new Error("Auth string does not match an existing user"), null);
|
callback("Auth string does not match an existing user", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +256,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof name !== "string" || typeof pw !== "string") {
|
if (typeof name !== "string" || typeof pw !== "string") {
|
||||||
callback(new Error("Invalid username/password combination"), null);
|
callback("Invalid username/password combination", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,7 +289,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof name !== "string") {
|
if (typeof name !== "string") {
|
||||||
callback(new Error("Invalid username"), null);
|
callback("Invalid username", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +298,7 @@ module.exports = {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err, null);
|
callback(err, null);
|
||||||
} else if (rows.length === 0) {
|
} else if (rows.length === 0) {
|
||||||
callback(new Error("User does not exist"), null);
|
callback("User does not exist", null);
|
||||||
} else {
|
} else {
|
||||||
callback(null, rows[0].global_rank);
|
callback(null, rows[0].global_rank);
|
||||||
}
|
}
|
||||||
|
@ -314,12 +314,12 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof name !== "string") {
|
if (typeof name !== "string") {
|
||||||
callback(new Error("Invalid username"), null);
|
callback("Invalid username", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof rank !== "number") {
|
if (typeof rank !== "number") {
|
||||||
callback(new Error("Invalid rank"), null);
|
callback("Invalid rank", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,7 +338,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(names instanceof Array)) {
|
if (!(names instanceof Array)) {
|
||||||
callback(new Error("Expected array of names, got " + typeof names), null);
|
callback("Expected array of names, got " + typeof names, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,7 +365,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof name !== "string") {
|
if (typeof name !== "string") {
|
||||||
callback(new Error("Invalid username"), null);
|
callback("Invalid username", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -374,7 +374,7 @@ module.exports = {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err, null);
|
callback(err, null);
|
||||||
} else if (rows.length === 0) {
|
} else if (rows.length === 0) {
|
||||||
callback(new Error("User does not exist"), null);
|
callback("User does not exist", null);
|
||||||
} else {
|
} else {
|
||||||
callback(null, rows[0].email);
|
callback(null, rows[0].email);
|
||||||
}
|
}
|
||||||
|
@ -390,12 +390,12 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof name !== "string") {
|
if (typeof name !== "string") {
|
||||||
callback(new Error("Invalid username"), null);
|
callback("Invalid username", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof email !== "string") {
|
if (typeof email !== "string") {
|
||||||
callback(new Error("Invalid email"), null);
|
callback("Invalid email", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,7 +414,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof name !== "string") {
|
if (typeof name !== "string") {
|
||||||
callback(new Error("Invalid username"), null);
|
callback("Invalid username", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,12 +423,18 @@ module.exports = {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err, null);
|
callback(err, null);
|
||||||
} else if (rows.length === 0) {
|
} else if (rows.length === 0) {
|
||||||
callback(new Error("User does not exist"), null);
|
callback("User does not exist", null);
|
||||||
} else {
|
} else {
|
||||||
var userprof = {
|
var userprof = {
|
||||||
image: "",
|
image: "",
|
||||||
text: ""
|
text: ""
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (rows[0].profile === "") {
|
||||||
|
callback(null, userprof);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var profile = JSON.parse(rows[0].profile);
|
var profile = JSON.parse(rows[0].profile);
|
||||||
userprof.image = profile.image || "";
|
userprof.image = profile.image || "";
|
||||||
|
@ -450,12 +456,12 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof name !== "string") {
|
if (typeof name !== "string") {
|
||||||
callback(new Error("Invalid username"), null);
|
callback("Invalid username", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof profile !== "object") {
|
if (typeof profile !== "object") {
|
||||||
callback(new Error("Invalid profile"), null);
|
callback("Invalid profile", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,7 +490,7 @@ module.exports = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(new Error("generatePasswordReset is not implemented"), null);
|
callback("generatePasswordReset is not implemented", null);
|
||||||
},
|
},
|
||||||
|
|
||||||
recoverPassword: function (hash, callback) {
|
recoverPassword: function (hash, callback) {
|
||||||
|
@ -492,7 +498,7 @@ module.exports = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(new Error("recoverPassword is not implemented"), null);
|
callback("recoverPassword is not implemented", null);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,7 +4,7 @@ var valid = require("../utilities").isValidChannelName;
|
||||||
var blackHole = function () { };
|
var blackHole = function () { };
|
||||||
|
|
||||||
function dropTable(name, callback) {
|
function dropTable(name, callback) {
|
||||||
db.query("DROP TABLE `" + name + "`");
|
db.query("DROP TABLE `" + name + "`", callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRanksTable(name, callback) {
|
function createRanksTable(name, callback) {
|
||||||
|
@ -116,7 +116,7 @@ module.exports = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid(chan)) {
|
if (!valid(name)) {
|
||||||
callback("Invalid channel name", null);
|
callback("Invalid channel name", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,12 @@ module.exports = {
|
||||||
callback(err, null);
|
callback(err, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
callback(null, rows);
|
|
||||||
|
if (rows.length === 0) {
|
||||||
|
callback("No such channel", null);
|
||||||
|
} else {
|
||||||
|
callback(null, rows[0]);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -212,7 +217,7 @@ module.exports = {
|
||||||
callback = blackHole;
|
callback = blackHole;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid(chan)) {
|
if (!valid(name)) {
|
||||||
callback("Invalid channel name", null);
|
callback("Invalid channel name", null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,15 +167,14 @@ function handleChangeEmail(req, res) {
|
||||||
* Handles a GET request for /account/channels
|
* Handles a GET request for /account/channels
|
||||||
*/
|
*/
|
||||||
function handleAccountChannelPage(req, res) {
|
function handleAccountChannelPage(req, res) {
|
||||||
res.send(500);
|
|
||||||
return;
|
|
||||||
logRequest(req);
|
logRequest(req);
|
||||||
var loginName = false;
|
var loginName = false;
|
||||||
if (req.cookies.auth) {
|
if (req.cookies.auth) {
|
||||||
loginName = req.cookies.auth.split(':')[0];
|
loginName = req.cookies.auth.split(':')[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loginName) {
|
if (loginName) {
|
||||||
dbchannels.listUserChannels(loginName, function (err, channels) {
|
db.channels.listUserChannels(loginName, function (err, channels) {
|
||||||
sendJade(res, 'account-channels', {
|
sendJade(res, 'account-channels', {
|
||||||
loggedIn: true,
|
loggedIn: true,
|
||||||
loginName: loginName,
|
loginName: loginName,
|
||||||
|
@ -194,8 +193,6 @@ function handleAccountChannelPage(req, res) {
|
||||||
* Handles a POST request to modify a user's channels
|
* Handles a POST request to modify a user's channels
|
||||||
*/
|
*/
|
||||||
function handleAccountChannel(req, res) {
|
function handleAccountChannel(req, res) {
|
||||||
res.send(500);
|
|
||||||
return;
|
|
||||||
logRequest(req);
|
logRequest(req);
|
||||||
var action = req.body.action;
|
var action = req.body.action;
|
||||||
switch(action) {
|
switch(action) {
|
||||||
|
@ -215,26 +212,162 @@ function handleAccountChannel(req, res) {
|
||||||
* Handles a request to register a new channel
|
* Handles a request to register a new channel
|
||||||
*/
|
*/
|
||||||
function handleNewChannel(req, res) {
|
function handleNewChannel(req, res) {
|
||||||
res.send(500);
|
logRequest(req);
|
||||||
|
|
||||||
|
var name = req.body.name;
|
||||||
|
if (typeof name !== 'string') {
|
||||||
|
res.send(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loginName = false;
|
||||||
|
if (req.cookies.auth) {
|
||||||
|
loginName = req.cookies.auth.split(':')[0];
|
||||||
|
} else {
|
||||||
|
sendJade(res, 'account-channels', {
|
||||||
|
loggedIn: false,
|
||||||
|
channels: []
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
db.users.verifyAuth(req.cookies.auth, function (err, user) {
|
||||||
|
if (err) {
|
||||||
|
sendJade(res, 'account-channels', {
|
||||||
|
loggedIn: false,
|
||||||
|
channels: [],
|
||||||
|
newChannelError: err
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
db.channels.register(name, user.name, function (err, channel) {
|
||||||
|
db.channels.listUserChannels(loginName, function (err2, channels) {
|
||||||
|
sendJade(res, 'account-channels', {
|
||||||
|
loggedIn: true,
|
||||||
|
loginName: loginName,
|
||||||
|
channels: err2 ? [] : channels,
|
||||||
|
newChannelError: err ? err : undefined
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a request to delete a new channel
|
* Handles a request to delete a new channel
|
||||||
*/
|
*/
|
||||||
function handleDeleteChannel(req, res) {
|
function handleDeleteChannel(req, res) {
|
||||||
|
logRequest(req);
|
||||||
|
|
||||||
|
var name = req.body.name;
|
||||||
|
if (typeof name !== 'string') {
|
||||||
|
res.send(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loginName = false;
|
||||||
|
if (req.cookies.auth) {
|
||||||
|
loginName = req.cookies.auth.split(':')[0];
|
||||||
|
} else {
|
||||||
|
sendJade(res, 'account-channels', {
|
||||||
|
loggedIn: false,
|
||||||
|
channels: [],
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
db.users.verifyAuth(req.cookies.auth, function (err, user) {
|
||||||
|
if (err) {
|
||||||
|
sendJade(res, 'account-channels', {
|
||||||
|
loggedIn: false,
|
||||||
|
channels: [],
|
||||||
|
deleteChannelError: err
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
db.channels.lookup(name, function (err, channel) {
|
||||||
|
if (channel.owner !== user.name && user.global_rank < 255) {
|
||||||
|
db.channels.listUserChannels(loginName, function (err2, channels) {
|
||||||
|
sendJade(res, 'account-channels', {
|
||||||
|
loggedIn: true,
|
||||||
|
loginName: loginName,
|
||||||
|
channels: err2 ? [] : channels,
|
||||||
|
deleteChannelError: 'You do not have permission to delete this channel'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
db.channels.drop(name, function (err) {
|
||||||
|
db.channels.listUserChannels(loginName, function (err2, channels) {
|
||||||
|
sendJade(res, 'account-channels', {
|
||||||
|
loggedIn: true,
|
||||||
|
loginName: loginName,
|
||||||
|
channels: err2 ? [] : channels,
|
||||||
|
deleteChannelError: err ? err : undefined
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a GET request for /account/profile
|
||||||
|
*/
|
||||||
|
function handleAccountProfilePage(req, res) {
|
||||||
|
logRequest(req);
|
||||||
|
|
||||||
|
var loginName = false;
|
||||||
|
if (req.cookies.auth) {
|
||||||
|
loginName = req.cookies.auth.split(':')[0];
|
||||||
|
} else {
|
||||||
|
sendJade(res, 'account-profile', {
|
||||||
|
loggedIn: false,
|
||||||
|
profileImage: '',
|
||||||
|
profileText: ''
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
db.users.getProfile(loginName, function (err, profile) {
|
||||||
|
if (err) {
|
||||||
|
sendJade(res, 'account-profile', {
|
||||||
|
loggedIn: true,
|
||||||
|
loginName: loginName,
|
||||||
|
profileError: err,
|
||||||
|
profileImage: '',
|
||||||
|
profileText: ''
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendJade(res, 'account-profile', {
|
||||||
|
loggedIn: true,
|
||||||
|
loginName: loginName,
|
||||||
|
profileImage: profile.image,
|
||||||
|
profileText: profile.text,
|
||||||
|
profileError: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a POST request to edit a profile
|
||||||
|
*/
|
||||||
|
function handleAccountProfile(req, res) {
|
||||||
res.send(500);
|
res.send(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
/**
|
/**
|
||||||
* Initializes the module
|
* Initialize the module
|
||||||
*
|
|
||||||
* @param app - The Express server to initialize
|
|
||||||
*/
|
*/
|
||||||
init: function (app) {
|
init: function (app) {
|
||||||
app.get('/account/edit', handleAccountEditPage);
|
app.get('/account/edit', handleAccountEditPage);
|
||||||
app.post('/account/edit', handleAccountEdit);
|
app.post('/account/edit', handleAccountEdit);
|
||||||
app.get('/account/channels', handleAccountChannelPage);
|
app.get('/account/channels', handleAccountChannelPage);
|
||||||
app.post('/account/channels', handleAccountChannel);
|
app.post('/account/channels', handleAccountChannel);
|
||||||
|
app.get('/account/profile', handleAccountProfilePage);
|
||||||
|
app.post('/account/profile', handleAccountProfile);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -40,6 +40,7 @@ html(lang="en")
|
||||||
th
|
th
|
||||||
form.form-inline.pull-right(action="/account/channels", method="post", onsubmit="return confirm('Are you sure you want to delete #{c.name}? This cannot be undone');")
|
form.form-inline.pull-right(action="/account/channels", method="post", onsubmit="return confirm('Are you sure you want to delete #{c.name}? This cannot be undone');")
|
||||||
input(type="hidden", name="action", value="delete_channel")
|
input(type="hidden", name="action", value="delete_channel")
|
||||||
|
input(type="hidden", name="name", value="#{c.name}")
|
||||||
button.btn.btn-xs.btn-danger(type="submit") Delete
|
button.btn.btn-xs.btn-danger(type="submit") Delete
|
||||||
span.glyphicon.glyphicon-trash
|
span.glyphicon.glyphicon-trash
|
||||||
a(href="/r/#{c.name}", style="margin-left: 5px")= c.name
|
a(href="/r/#{c.name}", style="margin-left: 5px")= c.name
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
doctype html
|
||||||
|
html(lang="en")
|
||||||
|
head
|
||||||
|
include head
|
||||||
|
mixin head()
|
||||||
|
body
|
||||||
|
#wrap
|
||||||
|
nav.navbar.navbar-inverse.navbar-fixed-top(role="navigation")
|
||||||
|
include nav
|
||||||
|
mixin navheader()
|
||||||
|
#nav-collapsible.collapse.navbar-collapse
|
||||||
|
ul.nav.navbar-nav
|
||||||
|
mixin navdefaultlinks("/account/profile")
|
||||||
|
mixin navloginlogout("/account/profile")
|
||||||
|
section#mainpage
|
||||||
|
.container
|
||||||
|
if !loggedIn
|
||||||
|
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
|
||||||
|
.alert.alert-danger.messagebox.center
|
||||||
|
strong Authorization Required
|
||||||
|
p You must be <a href="/login">logged in</a> to view this page.
|
||||||
|
else
|
||||||
|
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
|
||||||
|
h3 Profile
|
||||||
|
if profileError
|
||||||
|
.alert.alert-danger.center.messagebox
|
||||||
|
strong Profile Error
|
||||||
|
p= profileError
|
||||||
|
.media
|
||||||
|
a.pull-left(href="#")
|
||||||
|
img.media-object(src=profileImage, alt="Profile Image")
|
||||||
|
.media-body
|
||||||
|
= profileText
|
||||||
|
h3 Edit Profile
|
||||||
|
form(action="/account/profile", method="post", role="form")
|
||||||
|
.form-group
|
||||||
|
label.control-label(for="profileimage") Image
|
||||||
|
input#profileimage.form-control(type="text", name="image")
|
||||||
|
.form-group
|
||||||
|
label.control-label(for="profiletext") Text
|
||||||
|
textarea#profiletext.form-control(cols="10")
|
||||||
|
button.btn.btn-primary.btn-block(type="submit") Save
|
||||||
|
|
||||||
|
include footer
|
||||||
|
mixin footer()
|
Loading…
Reference in New Issue