Make autologin working and add logos and manifes.json for webapp

parent 710ed0fa
Web/resources/img/favicon.ico

318 Bytes | W: | H:

Web/resources/img/favicon.ico

31.3 KB | W: | H:

Web/resources/img/favicon.ico
Web/resources/img/favicon.ico
Web/resources/img/favicon.ico
Web/resources/img/favicon.ico
  • 2-up
  • Swipe
  • Onion skin
......@@ -5,6 +5,18 @@
<meta name="viewport" content="width=device-width, initial-scale=0.85, maximum-scale=0.85, minimum-scale=0.85, user-scalable=no" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=0.85" />
<link rel="manifest" href="/manifest.json">
<!-- Allow fullscreen mode on iOS devices. (These are Apple specific meta tags.) -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="apple-touch-icon" sizes="256x256" href="/resources/img/logo_icon.png" />
<meta name="HandheldFriendly" content="true" />
<!-- Chrome for Android web app tags -->
<meta name="mobile-web-app-capable" content="yes">
<link rel="shortcut icon" sizes="256x256" href="/resources/img/logo_icon.png" />
<style>
body {
background: none repeat scroll 0 0 #FBFBFB;
......
{
"short_name": "Penguidom",
"name": "Domotika device client",
"icons": [
{
"src": "/resources/img/logo_icon.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "/resources/img/logo_icon-128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "/resources/img/logo_icon-64.png",
"sizes": "64x64",
"type": "image/png"
},
{
"src": "/resources/img/logo_icon-32.png",
"sizes": "32x32",
"type": "image/png"
}
],
"start_url": "/gui/",
"display": "fullscreen",
"orientation": "portrait",
"developer": {
"name": "Franco (nextime) Lanza",
"url": "https://www.nexlab.net"
}
}
<html>
<head>
<title>Penguidom GUI</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<link rel="manifest" href="/manifest.json">
<!-- Allow fullscreen mode on iOS devices. (These are Apple specific meta tags.) -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="apple-touch-icon" sizes="256x256" href="/resources/img/logo_icon.png" />
<meta name="HandheldFriendly" content="true" />
<!-- Chrome for Android web app tags -->
<meta name="mobile-web-app-capable" content="yes">
<link rel="shortcut icon" sizes="256x256" href="/resources/img/logo_icon.png" />
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<style>
body {
......@@ -23,28 +34,25 @@ body {
/* Style the navbar */
#navbar {
z-index: 10000;
overflow: hidden;
background-color: #333;
}
/* Navbar links */
#navbar a {
float: left;
padding: 14px;
display: block;
color: #f2f2f2;
text-align: center;
padding: 14px;
text-decoration: none;
}
/* Page content */
.content {
#content {
padding: 16px;
}
transition: margin-left .5s, margin-right .5s ;
#main {
transition: margin-left .5s;
transition: margin-right .5s;
}
/* The sticky class is added to the navbar with JS when it reaches its scroll position */
......@@ -65,22 +73,36 @@ body {
height: 100%; /* 100% Full-height */
width: 0; /* 0 width - change this with JavaScript */
position: fixed; /* Stay in place */
z-index: 1000; /* Stay on top */
top: 0; /* Stay at the top */
left: 0;
z-index: 10; /* Stay on top */
top: 52; /* Stay at the top */
background-color: #111; /* Black*/
overflow-x: hidden; /* Disable horizontal scroll */
padding-top: 60px; /* Place content 60px from the top */
padding-top: 20px; /* Place content 60px from the top */
transition: 0.5s; /* 0.5 second transition effect to slide in the sidenav */
}
/* Position and style the close button (top right corner) */
.sidenav .closebtn {
position: absolute;
top: 0;
right: 25px;
font-size: 36px;
margin-left: 50px;
/* The navigation menu links */
.sidenav a {
padding: 8px 8px 8px 32px;
text-decoration: none;
font-size: 25px;
color: #818181;
display: block;
transition: 0.3s;
}
/* When you mouse over the navigation links, change their color */
.sidenav a:hover {
color: #f1f1f1;
}
.sidenavright {
right:0;
}
.sidenavleft {
left:0;
}
.column {
......@@ -97,7 +119,12 @@ body {
display: table;
}
#navbar #navbtnl {
.navrow:after {
content: "";
display: table;
}
#navbtnl {
float: left;
width: 115px;
height: 38px;
......@@ -106,19 +133,23 @@ body {
margin: 6px 6px 6px 6px;
}
#navbar #navbtnl a {
#navbtnl a {
position: fixed;
margin-left:30px;
top: 0px;
}
#navbar #navbtnr a {
#navbtnr a {
position: fixed;
margin-right:30px;
top: 0px;
}
#navbar #navbtnr {
#navcntr a {
padding-top: 0;
}
#navbtnr {
float: right;
width: 115px;
height: 38px;
......@@ -127,12 +158,10 @@ body {
margin: 6px 6px 6px 6px;
}
#navbar #navcntr {
#navcntr {
position: fixed;
width: 100%;
margin-right: 90px;
margin-left: 90px;
left: -105px;
right: 90px;
left: 90px;
text-align: center;
color: #f2f2f2;
padding: 14px;
......@@ -151,42 +180,61 @@ div .menubtn div {
margin: 4px 0;
}
.simplebtn {
border: 1px solid #f2f2f2;
border-radius: 10px;
text-align: center;
padding: 6px;
margin: 4px 16px 0px 16px;
background-color: #0d1f29;
}
</style>
<script language="javascript">
sideSize = "400px";
if ( window.innerWidth <= parseInt(sideSize)+200) {
sideSize = "100%";
}
function OpenLeft() {
if(document.getElementById("SidenavLeft").style.width == sideSize) {
CloseNav();
} else {
document.getElementById("navbtnl").style.border = "solid 1px #19abe2";
document.getElementById("navbtnr").style.border = "solid 1px #f2f2f2";
document.getElementById("SidenavRight").style.width = "0px";
document.getElementById("SidenavLeft").style.width = "250px";
document.getElementById("SidenavLeft").style.width = sideSize;
document.getElementById("content").style.marginLeft = sideSize;
document.getElementById("content").style.marginRight = "0px";
}
}
function OpenRight() {
if(document.getElementById("SidenavRight").style.width == sideSize) {
CloseNav();
} else {
document.getElementById("SidenavLeft").style.width = "0px";
document.getElementById("SidenavRight").style.width = "250px";
document.getElementById("SidenavRight").style.width = sideSize;
document.getElementById("content").style.marginLeft = "0px";
document.getElementById("content").style.marginRight = sideSize;
document.getElementById("navbtnl").style.border = "solid 1px #f2f2f2";
document.getElementById("navbtnr").style.border = "solid 1px #19abe2";
}
}
function CloseNav() {
document.getElementById("SidenavLeft").style.width = "0px";
document.getElementById("SidenavRight").style.width = "0px";
document.getElementById("content").style.marginRight = "0px";
document.getElementById("content").style.marginLeft = "0px";
document.getElementById("navbtnl").style.border = "solid 1px #f2f2f2";
document.getElementById("navbtnr").style.border = "solid 1px #f2f2f2";
}
</script>
@SCRIPT@
</head>
<body class="tblack">
<div id="SidenavLeft" class="sidenav">
<a href="javascript:void(0)" class="closebtn" onclick="CloseNav()">&times;</a>
<a href="#">About</a>
<a href="#">Services</a>
<a href="#">Clients</a>
<a href="#">Contact</a>
</div>
<div id="SidenavRight" class="sidenav">
<a href="javascript:void(0)" class="closebtn" onclick="CloseNav()">&times;</a>
<a href="#">About</a>
<a href="#">Services</a>
<a href="#">Clients</a>
<a href="#">Contact</a>
</div>
<div id="main">
<div id="navbar" class="row">
<div id="navbar" class="navrow">
<div id="navcntr">
<b>Penguidom 0.1</b>
<b><a href="/gui/">Penguidom 0.1</a></b>
</div>
<div id="navbtnl" onclick="OpenLeft()">
......@@ -205,15 +253,24 @@ function CloseNav() {
</div>
<a href="#">options</a>
</div>
</div>
<div id="content" class="tblack" >
</div>
<div id="SidenavLeft" class="sidenav sidenavleft">
<a href="#">About</a>
<a href="#">Services</a>
<a href="#">Clients</a>
<a href="#">Contact</a>
</div>
<div id="SidenavRight" class="sidenav sidenavright">
<div class="simplebtn"><a href="/__LOGOUT__/">Logout</a></div>
</div>
<div id="content" class="tblack" >
<div class="row">
<div class="column"></div>
<div class="column"></div>
<div class="column"></div>
</div>
@CONTENT@
</div>
</div>
</body>
</html>
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 64 64"
enable-background="new 0 0 64 64"
version="1.1"
id="svg12"
sodipodi:docname="penguindom.svg"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)">
<metadata
id="metadata18">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs16" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1058"
id="namedview14"
showgrid="false"
inkscape:zoom="7.375"
inkscape:cx="70.195191"
inkscape:cy="30.365634"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg12" />
<g
id="g3724"
transform="matrix(0.66442162,0,0,0.70277545,10.011228,4.284375)">
<path
id="path2"
d="M 53.615,27.025 C 51.517,25.722 49.84,24.885 48.552,24.435 48.223,22.429 48,20.162 48,17.467 48,8.926 40.836,2 32,2 23.164,2 16,8.926 16,17.467 c 0,2.695 -0.221,4.962 -0.553,6.969 -1.286,0.449 -2.964,1.287 -5.063,2.59 C -0.38,33.705 1.73,43.048 3.25,41.941 7.307,38.988 10.616,36.095 13.119,33.521 12.488,35.765 12,37.985 12,40.667 c 0,6.745 3.578,12.677 8.995,16.135 0.197,1.171 0.449,3.271 -0.15,3.841 -0.576,0.545 -2.362,-0.115 -3.176,-0.115 C 15.642,60.527 14,62 14,62 h 16.002 c 0,0 -1.643,-1.473 -3.668,-1.473 -0.814,0 -2.602,0.66 -3.176,0.115 C 22.711,60.22 22.736,58.954 22.859,57.849 25.602,59.215 28.703,60 32,60 c 3.297,0 6.398,-0.784 9.139,-2.149 0.123,1.104 0.148,2.372 -0.297,2.794 C 40.268,61.19 38.481,60.53 37.666,60.53 35.643,60.529 34,62 34,62 h 16 c 0,0 -1.643,-1.471 -3.668,-1.471 -0.813,0 -2.6,0.66 -3.176,0.115 -0.6,-0.569 -0.348,-2.671 -0.15,-3.842 C 48.422,53.344 52,47.413 52,40.667 c 0,-2.682 -0.486,-4.902 -1.119,-7.146 2.504,2.574 5.813,5.467 9.869,8.42 1.52,1.107 3.631,-8.236 -7.135,-14.916 M 32,23.035 27.512,21.94 c 0.172,-2.697 2.119,-4.831 4.488,-4.831 2.367,0 4.314,2.134 4.486,4.831 L 32,23.035 m 3.328,0.218 c -0.453,1.65 -1.766,2.856 -3.328,2.856 -1.566,0 -2.877,-1.206 -3.33,-2.857 L 32,24.064 35.328,23.253 M 32,58.066 c -8.283,0 -16,-5.578 -16,-17.144 0,-6.423 4,-12.466 4,-22.134 C 20,7.106 28.957,8.58 30.008,16.537 27.957,17.442 26.5,19.694 26.5,22.329 v 0.393 l 1.109,0.271 c 0.41,2.337 2.201,4.116 4.391,4.116 2.188,0 3.979,-1.779 4.387,-4.115 L 37.5,22.723 V 22.33 c 0,-2.635 -1.459,-4.887 -3.508,-5.792 C 35.041,8.58 44,7.108 44,18.789 c 0,9.668 4,15.711 4,22.134 0,11.565 -7.715,17.143 -16,17.143"
inkscape:connector-curvature="0" />
<ellipse
id="ellipse4"
ry="3"
rx="2"
cy="16.969"
cx="39" />
<path
id="path6"
d="m 24.998,13.969 c -1.102,0 -1.998,1.344 -1.998,2.998 0,1.658 0.896,3.002 1.998,3.002 1.107,0 2.002,-1.344 2.002,-3.002 0,-1.654 -0.895,-2.998 -2.002,-2.998"
inkscape:connector-curvature="0" />
<path
id="path8"
d="m 32.973,17.543 c -0.145,0.166 -0.031,0.563 0.254,0.889 0.281,0.325 0.629,0.455 0.773,0.289 0.145,-0.164 0.031,-0.563 -0.252,-0.888 -0.283,-0.325 -0.631,-0.456 -0.775,-0.29"
inkscape:connector-curvature="0" />
<path
id="path10"
d="m 30.25,17.833 c -0.285,0.325 -0.396,0.724 -0.254,0.888 0.146,0.166 0.492,0.036 0.777,-0.289 0.283,-0.325 0.396,-0.723 0.254,-0.889 -0.144,-0.166 -0.494,-0.035 -0.777,0.29"
inkscape:connector-curvature="0" />
<text
transform="scale(0.95672851,1.0452286)"
id="text3711"
y="49.213985"
x="25.635483"
style="font-style:normal;font-weight:normal;font-size:26.69284248px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.66732109px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Italic';stroke-width:0.66732109px"
y="49.213985"
x="25.635483"
id="tspan3709"
sodipodi:role="line">P</tspan></text>
</g>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.00685406px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.30017135px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="0.15441999"
y="57.257843"
id="text3715"
transform="scale(0.948691,1.054084)"><tspan
sodipodi:role="line"
id="tspan3713"
x="0.15441999"
y="57.257843"
style="stroke-width:0.30017135px">Penguidom</tspan></text>
<g
id="g4187"
transform="matrix(0.17062405,0,0,0.13128431,75.446253,3.0853336)">
<g
id="g4131">
<path
id="path4129"
d="m -143.19864,358.44538 h -166.47805 c -1.78698,0 -3.23215,-1.49611 -3.23587,-3.34131 l -8.7e-4,-0.49501 c 0.81505,-2.60583 2.05907,-2.73132 6.47272,-2.84628 v 0 c 54.41407,0 108.82812,0 163.2422,0 15.55099,0 28.15638,-13.066 28.15638,-29.07481 V 125.90025 L -256.35733,-7.5523349 -397.7212,125.90025 v 177.23861 c 0,1.84468 -1.44889,3.3408 -3.23586,3.3408 -1.78698,0 -3.23587,-1.49612 -3.23587,-3.3408 V 124.40415 c 0,-0.94753 0.38638,-1.8452 1.06252,-2.44365 l 144.59974,-136.444788 c 1.20741,-1.147013 3.13927,-1.147013 4.34669,0 L -109.63206,121.9605 c 0.67615,0.64832 1.06204,1.54599 1.06204,2.44365 v 198.28382 c -4.8e-4,19.69921 -15.55147,35.75741 -34.62859,35.75741 z"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssccccscccssssccccscs"
style="stroke-width:0.49077028" />
</g>
<g
id="g4133" />
<g
id="g4135" />
<g
id="g4137" />
<g
id="g4139" />
<g
id="g4141" />
<g
id="g4143" />
<g
id="g4145" />
<g
id="g4147" />
<g
id="g4149" />
<g
id="g4151" />
<g
id="g4153" />
<g
id="g4155" />
<g
id="g4157" />
<g
id="g4159" />
<g
id="g4161" />
</g>
</svg>
......@@ -69,7 +69,7 @@ class clientAuth(object):
def checkAuth(self, usr, pwd):
log.debug("CheckAuth for "+str(usr))
if usr in self.users.keys():
return defer.succeed((genutils.hashPwd256("".join([self.users[usr]['salt'], pwd])) == self.users[usr]['hash']))
return defer.succeed(((genutils.hashPwd256("".join([self.users[usr]['salt'], pwd])) == self.users[usr]['hash']) or (pwd == self.users[usr]['hash'])))
return defer.succeed(False)
def getAuth(self, usr, pwd):
......@@ -84,5 +84,20 @@ class clientAuth(object):
def AvatarResults(self, res, c):
log.debug("AvatarResults "+str(res)+" "+str(c.username))
if res:
c.salt = self.users[c.username]["salt"]
c.hash = self.users[c.username]["hash"]
return defer.succeed([c, res])
raise cred.error.UnauthorizedLogin()
def getUserLine(user):
with open(USERFILE) as fp:
for cnt, line in enumerate(fp):
if line.startswith(user):
try:
l = line.split(':')
return {'user': l[0], 'salt': l[1], 'hash': l[2].rstrip("\r\n")}
except:
pass
return False
......@@ -90,6 +90,7 @@ class BasePage(rend.Page):
self.putChild('favicon.ico', static.File(curdir+'/Web/resources/img/favicon.ico'))
self.putChild('crossdomain.xml', static.File(curdir+'/Web/resources/xml/crossdomain.xml'))
self.putChild('sockjs.min.js', static.File(curdir+'/Web/resources/js/sockjs.min.js'))
self.putChild('manifest.json', static.File(curdir+'/Web/resources/manifest.json'))
def locateChild(self, ctx, name):
log.debug("LocateChild in BasePage (%s)" % str(name))
......@@ -325,7 +326,7 @@ class RootAuthPage(RootPage):
super(RootAuthPage, self).__init__(self)
self.logged=True
self.mind = mind
self.perms = avatarId[1]
self.perms = avatarId[0]
def logout(self):
log.debug("LOGOUT CALLED")
......@@ -340,14 +341,14 @@ class RootAuthPage(RootPage):
self.mind.perms = self.perms
if 'rememberMe' in self.mind.args:
log.debug("Setting rememberMe cookie for avatar "+str(self.avatarId))
rme=dmcrypt.B64AESEncrypt(str(self.core.configGet('web', 'cookie_aeskey')), self.perms.passwd,
" ".join([self.perms.username, self.perms.loginpwd, self.perms.passwd]))
rme=dmcrypt.B64AESEncrypt(str(self.core.configGet('web', 'cookie_aeskey')), self.perms.salt,
" ".join([self.perms.username, self.perms.hash, self.perms.salt]))
try:
expire=http.datetimeToString(time.time() + 3600*24*365*50)
except:
expire=http.datetimeToString(time.time() + 3600*24*365*10)
request.addCookie("Penguidom_rme", str(self.perms.id)+':'+rme,
request.addCookie("Penguidom_rme", str(self.perms.username)+':'+rme,
path="/", secure=True, expires=expire)
return RootPage.locateChild(self, ctx, name)
......@@ -386,6 +387,7 @@ class LoginPage(rend.Page):
def __init__(self, *a, **kw):
self.putChild('favicon.ico', static.File(curdir+'/Web/resources/img/favicon.ico'))
self.putChild('manifest.json', static.File(curdir+'/Web/resources/manifest.json'))
if os.path.isfile(LOGINFILE):
try:
lf = open(LOGINFILE, "r")
......@@ -416,12 +418,9 @@ class LoginPage(rend.Page):
rmec=str(request.getCookie("Penguidom_rme"))
log.info("RememberMe COOKIE FOUND: "+rmec)
rmecl = rmec.split(':')
try:
if len(rmecl) > 1:
uid = str(int(rmecl[0]))
rme = self.core.getUserFromID(uid)
except:
pass
user = str(rmecl[0])
rme = auth.getUserLine(user)
if request.path.startswith("/rest/") and '/keepalive' in request.path:
......@@ -433,7 +432,7 @@ class LoginPage(rend.Page):
return self.getStandardHTML(request.uri)
else:
log.info("LOGIN FROM COOKIE FOR PATH "+request.uri)
return rme.addCallback(self.rmelogin, request, rmec)
return self.rmelogin(rme, request, rmec)
def getStandardHTML(self, path):
......@@ -446,20 +445,15 @@ class LoginPage(rend.Page):
return html
def getScript(self, path):
return '<script> window.onload=function(){ window.setTimeout(function() {document.loginform.submit();}, 1000); };</script>'
return '<script> window.onload=function(){ window.setTimeout(function() {document.loginform.submit();}, 200); };</script>'
def rmelogin(self, res, req, has):
if res and (('__len__' in dir(res) and len(res) > 0) or res!=None ) and len(has.split(":", 1)) > 1:
if '__len__' in dir(res):
user=res[0]
else:
user=res
def rmelogin(self, userq, req, has):
rme=dmcrypt.B64AESDecrypt(str(self.core.configGet('web', 'cookie_aeskey')), user.passwd, has.split(":", 1)[1])
rme=dmcrypt.B64AESDecrypt(str(self.core.configGet('web', 'cookie_aeskey')), userq['salt'], has.split(":", 1)[1])
if len(rme.split()) == 3:
u, lp, p = rme.split()
if user.username == u and user.passwd == p:
log.info("Cookie login succeed for user "+user.username)
u, h, s = rme.split()
if userq['user'] == u and userq['hash'] == h:
log.info("Cookie login succeed for user "+userq['user'])
try:
expire=http.datetimeToString(time.time() + 3600*24*365*50)
except:
......@@ -469,11 +463,10 @@ class LoginPage(rend.Page):
path="/", secure=True, expires=expire)
html = self.html.replace("@PATH@", '/__login__'+req.uri)
html = html.replace("@USERNAME@", str(user.username))
html = html.replace("@PASSWORD@", str(lp))
html = html.replace("@USERNAME@", str(userq['user']))
html = html.replace("@PASSWORD@", str(userq['hash']))
html = html.replace("@CHECKED@", "checked")
html = html.replace("@SCRIPT@", str(self.getScript(req.path)))
html = html.replace("@THEME@", str(self.core.configGet('web', 'logintheme')))
log.debug("login html")
return html
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment