Removed ui module - this is just the old console from IdentityBroker
22
ui/pom.xml
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<project>
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.0-alpha-1</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-ui</artifactId>
|
||||
<name>Keycloak UI</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>jaxrs-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -1,230 +0,0 @@
|
|||
package org.keycloak.ui.example;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.ws.rs.ApplicationPath;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
@ApplicationPath("ui/api")
|
||||
@Path("")
|
||||
public class Admin extends javax.ws.rs.core.Application {
|
||||
|
||||
private static Map<String, Application> applications = new HashMap<String, Application>();
|
||||
|
||||
private static Map<String, Realm> realms = new HashMap<String, Realm>();
|
||||
|
||||
private static Map<String, List<User>> users = new HashMap<String, List<User>>();
|
||||
|
||||
private static Map<Id, List<String>> roles = new HashMap<Id, List<String>>();
|
||||
|
||||
static {
|
||||
Realm realm = new Realm();
|
||||
realm.setId(UUID.randomUUID().toString());
|
||||
realm.setName("Test realm");
|
||||
realm.setEnabled(true);
|
||||
realm.setRoles(new String[] { "admin", "user" });
|
||||
realms.put(realm.getId(), realm);
|
||||
|
||||
User user = new User();
|
||||
user.setUserId("user");
|
||||
user.setPassword("password");
|
||||
|
||||
List<User> l = new LinkedList<User>();
|
||||
l.add(user);
|
||||
|
||||
users.put(realm.getId(), l);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("applications/{id}")
|
||||
public void deleteApplication(@PathParam("id") String id) {
|
||||
applications.remove(id);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("realms/{id}")
|
||||
public void deleteRealm(@PathParam("id") String id) {
|
||||
realms.remove(id);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("applications/{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Application getApplication(@PathParam("id") String id) {
|
||||
return applications.get(id);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("applications")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public List<Application> getApplications() {
|
||||
return new LinkedList<Application>(applications.values());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("realms/{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Realm getRealm(@PathParam("id") String id) {
|
||||
return realms.get(id);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("realms")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public List<Realm> getRealms() {
|
||||
return new LinkedList<Realm>(realms.values());
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("applications")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Response save(Application application) {
|
||||
String id = UUID.randomUUID().toString();
|
||||
application.setId(id);
|
||||
applications.put(id, application);
|
||||
return Response.created(URI.create("/applications/" + id)).build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("realms")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Response save(Realm realm) {
|
||||
String id = UUID.randomUUID().toString();
|
||||
realm.setId(id);
|
||||
realms.put(id, realm);
|
||||
return Response.created(URI.create("/realms/" + id)).build();
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("applications/{id}")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public void save(@PathParam("id") String id, Application application) {
|
||||
try {
|
||||
deleteApplication(id);
|
||||
} catch (WebApplicationException e) {
|
||||
}
|
||||
|
||||
applications.put(id, application);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("realms/{id}")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public void save(@PathParam("id") String id, Realm realm) {
|
||||
try {
|
||||
deleteRealm(id);
|
||||
} catch (WebApplicationException e) {
|
||||
}
|
||||
|
||||
realms.put(id, realm);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("realms/{realm}/users/{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public User getUser(@PathParam("realm") String realm, @PathParam("id") String id) {
|
||||
for (User u : getUsers(realm)) {
|
||||
if (u.getUserId().equals(id)) {
|
||||
return u;
|
||||
}
|
||||
}
|
||||
throw new WebApplicationException(Status.NOT_FOUND);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("realms/{realm}/users")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public List<User> getUsers(@PathParam("realm") String realm) {
|
||||
List<User> l = users.get(realm);
|
||||
if (l == null) {
|
||||
l = new LinkedList<User>();
|
||||
users.put(realm, l);
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("realms/{realm}/users/{id}")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public void save(@PathParam("realm") String realm, @PathParam("id") String id, User user) {
|
||||
try {
|
||||
deleteUser(realm, id);
|
||||
} catch (WebApplicationException e) {
|
||||
}
|
||||
|
||||
getUsers(realm).add(user);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("realms/{realm}/users/{id}")
|
||||
public void deleteUser(@PathParam("realm") String realm, @PathParam("id") String id) {
|
||||
getUsers(realm).remove(getUser(realm, id));
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("roles/{realm}/{role}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public List<User> getRoleMapping(@PathParam("realm") String realm, @PathParam("role") String role) {
|
||||
List<String> ids = getRoleMapping(new Id(realm, role));
|
||||
|
||||
List<User> users = new LinkedList<User>();
|
||||
List<User> realmUsers = getUsers(realm);
|
||||
for (String id : ids) {
|
||||
for (User u : realmUsers) {
|
||||
if (u.getUserId().equals(id)) {
|
||||
users.add(u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
private List<String> getRoleMapping(Id id) {
|
||||
List<String> l = roles.get(id);
|
||||
if (l == null) {
|
||||
l = new LinkedList<String>();
|
||||
roles.put(id, l);
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("roles/{realm}/{role}/{user}")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public void addRoleMapping(@PathParam("realm") String realm, @PathParam("role") String role,
|
||||
@PathParam("user") String user, User u) {
|
||||
getRoleMapping(new Id(realm, role)).add(user);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("roles/{realm}/{role}/{user}")
|
||||
public void deleteRoleMapping(@PathParam("realm") String realm, @PathParam("role") String role,
|
||||
@PathParam("user") String user) {
|
||||
Iterator<String> itr = getRoleMapping(new Id(realm, role)).iterator();
|
||||
while (itr.hasNext()) {
|
||||
if (itr.next().equals(user)) {
|
||||
itr.remove();
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new WebApplicationException(Status.NOT_FOUND);
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2012, Red Hat, Inc., and individual contributors
|
||||
* as indicated by the @author tags. See the copyright.txt file in the
|
||||
* distribution for a full listing of individual contributors.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this software; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||
*/
|
||||
package org.keycloak.ui.example;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
@XmlRootElement
|
||||
public class Application {
|
||||
|
||||
private String[] callbackUrl;
|
||||
|
||||
private boolean enabled;
|
||||
|
||||
private String id;
|
||||
|
||||
private String[] initialRoles;
|
||||
|
||||
private String name;
|
||||
|
||||
private String realm;
|
||||
|
||||
private String[] roles;
|
||||
|
||||
public String[] getCallbackUrl() {
|
||||
return callbackUrl;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String[] getInitialRoles() {
|
||||
return initialRoles;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
public String[] getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setCallbackUrl(String[] callbackUrl) {
|
||||
this.callbackUrl = callbackUrl;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setInitialRoles(String[] initialRoles) {
|
||||
this.initialRoles = initialRoles;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setRealm(String realm) {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public void setRoles(String[] roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package org.keycloak.ui.example;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
@XmlRootElement
|
||||
public class Attribute implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String name;
|
||||
|
||||
private String value;
|
||||
|
||||
public Attribute() {
|
||||
}
|
||||
|
||||
public Attribute(String name, String value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
package org.keycloak.ui.example;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Id {
|
||||
|
||||
private String[] id;
|
||||
|
||||
public Id(String... id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String[] getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getId(int i) {
|
||||
return id[i];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + Arrays.hashCode(id);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Id other = (Id) obj;
|
||||
if (!Arrays.equals(id, other.id))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2012, Red Hat, Inc., and individual contributors
|
||||
* as indicated by the @author tags. See the copyright.txt file in the
|
||||
* distribution for a full listing of individual contributors.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this software; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||
*/
|
||||
package org.keycloak.ui.example;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
@XmlRootElement
|
||||
public class IdentityProviderConfig {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String key;
|
||||
|
||||
private String providerId;
|
||||
|
||||
private String secret;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public String getProviderId() {
|
||||
return providerId;
|
||||
}
|
||||
|
||||
public String getSecret() {
|
||||
return secret;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public void setProviderId(String providerId) {
|
||||
this.providerId = providerId;
|
||||
}
|
||||
|
||||
public void setSecret(String secret) {
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2012, Red Hat, Inc., and individual contributors
|
||||
* as indicated by the @author tags. See the copyright.txt file in the
|
||||
* distribution for a full listing of individual contributors.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this software; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||
*/
|
||||
package org.keycloak.ui.example;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class Realm {
|
||||
|
||||
private boolean enabled;
|
||||
|
||||
private String[] initialRoles;
|
||||
|
||||
private String id;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
private String name;
|
||||
|
||||
private String[] roles;
|
||||
|
||||
private boolean social;
|
||||
|
||||
private long tokenExpiration;
|
||||
|
||||
private TimeUnit tokenExpirationUnit;
|
||||
|
||||
private boolean userRegistration;
|
||||
|
||||
public String[] getInitialRoles() {
|
||||
return initialRoles;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String[] getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public long getTokenExpiration() {
|
||||
return tokenExpiration;
|
||||
}
|
||||
|
||||
public TimeUnit getTokenExpirationUnit() {
|
||||
return tokenExpirationUnit;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public boolean isSocial() {
|
||||
return social;
|
||||
}
|
||||
|
||||
public boolean isUserRegistration() {
|
||||
return userRegistration;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public void setInitialRoles(String[] initialRoles) {
|
||||
this.initialRoles = initialRoles;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setRoles(String[] roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
public void setSocial(boolean social) {
|
||||
this.social = social;
|
||||
}
|
||||
|
||||
public void setTokenExpiration(long tokenExpiration) {
|
||||
this.tokenExpiration = tokenExpiration;
|
||||
}
|
||||
|
||||
public void setTokenExpirationUnit(TimeUnit tokenExpirationUnit) {
|
||||
this.tokenExpirationUnit = tokenExpirationUnit;
|
||||
}
|
||||
|
||||
public void setUserRegistration(boolean userRegistration) {
|
||||
this.userRegistration = userRegistration;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source
|
||||
* Copyright 2012, Red Hat, Inc., and individual contributors
|
||||
* by the @authors tag. See the copyright.txt in the distribution for a
|
||||
* full listing of individual contributors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.ui.example;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
@XmlRootElement
|
||||
public class User implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private List<Attribute> attributes;
|
||||
private String email;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private String userId;
|
||||
private String password;
|
||||
private String[] roles;
|
||||
|
||||
public String[] getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(String[] roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public List<Attribute> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setAttributes(List<Attribute> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,314 +0,0 @@
|
|||
/* General styles */
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
|
||||
#actions-bg {width: 1150px;}
|
||||
|
||||
#container-right-bg {
|
||||
margin-left: 300px;
|
||||
width: 900px;
|
||||
}
|
||||
|
||||
.span9 {
|
||||
width: 840px;
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
.initial {margin-left: 160px;}
|
||||
|
||||
.form-mobile-redirect .header-mobile-redirect .control-group,
|
||||
.form-mobile-redirect .header-mobile-redirect .control-group:last-child {
|
||||
width: 300px;
|
||||
margin-left: 60px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.header-mobile-redirect input, .header-mobile-redirect select {width: 175px;}
|
||||
|
||||
tbody.sortable tr td:nth-child(1) {width: 150px;}
|
||||
tbody.sortable tr td:nth-child(2) {width: 420px;}
|
||||
tbody.sortable tr td:nth-child(3v) {width: 130px;}
|
||||
|
||||
#mappings .input-medium {width: 180px;}
|
||||
|
||||
footer {width: 1170px;}
|
||||
|
||||
footer p {margin-left: 300px;}
|
||||
|
||||
footer > ul {margin-right: 25px;}
|
||||
|
||||
}
|
||||
|
||||
@media (min-width: 1025px) {
|
||||
|
||||
td.actions > a,
|
||||
td.actions > button,
|
||||
td.actions > div {visibility: hidden}
|
||||
|
||||
tr:hover > td.actions > a,
|
||||
tr:hover > td.actions > button,
|
||||
tr:hover > td.actions > div {visibility: visible;}
|
||||
|
||||
}
|
||||
|
||||
@media (min-width: 768px) and (max-width: 979px) {
|
||||
|
||||
body {padding-top: 0;}
|
||||
|
||||
/* Topbar */
|
||||
|
||||
.navbar-fixed-top {
|
||||
margin-bottom: 2px;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.navbar-fixed-top .navbar-inner {padding: 0;}
|
||||
|
||||
.navbar .container {width: 724px;}
|
||||
|
||||
.navbar .brand {margin-left: -20px;}
|
||||
|
||||
.nav-collapse {
|
||||
height: auto;
|
||||
overflow: visible;
|
||||
clear: none;
|
||||
}
|
||||
|
||||
.nav-collapse .nav {margin-bottom: 0;}
|
||||
|
||||
.navbar .nav-collapse .nav.pull-right {float: right;}
|
||||
|
||||
.nav-collapse .nav > li {float: left;}
|
||||
|
||||
.nav-collapse .nav > li > a {
|
||||
font-weight: normal;
|
||||
padding: 9px 10px 11px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Topbar dropdown */
|
||||
|
||||
.navbar .dropdown-menu {display: none;}
|
||||
|
||||
.open .dropdown-menu,
|
||||
.open .dropdown-menu:before,
|
||||
.open .dropdown-menu:after {display: block;}
|
||||
|
||||
.open .dropdown-menu {
|
||||
background-color: #FFFFFF;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
border-radius: 5px 5px 5px 5px;
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
margin: 1px 0 0;
|
||||
padding: 4px 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.nav-collapse .dropdown-menu a {
|
||||
color: #333333;
|
||||
line-height: 18px;
|
||||
padding: 3px 15px;
|
||||
border-radius: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.nav-collapse .dropdown-menu li > a:hover,
|
||||
.nav-collapse .dropdown-menu .active > a,
|
||||
.nav-collapse .dropdown-menu .active > a:hover {
|
||||
background-color: #0088CC;
|
||||
color: #FFFFFF;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Aside */
|
||||
|
||||
.nav-list a#add-page {top: 15px;}
|
||||
|
||||
#actions-bg {width: 730px;}
|
||||
|
||||
/* Content */
|
||||
|
||||
.form-horizontal .control-label {width: 125px;}
|
||||
|
||||
.form-horizontal .controls {margin-left: 140px;}
|
||||
|
||||
#container-right-bg {
|
||||
margin-left: 186px;
|
||||
width: 558px;
|
||||
}
|
||||
|
||||
.span9 {
|
||||
width: 518px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.initial {margin-left: 0;}
|
||||
|
||||
.header-mobile-redirect .control-group label {
|
||||
width: 100px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.form-mobile-redirect .header-mobile-redirect .control-group {width: 300px;}
|
||||
|
||||
.form-mobile-redirect .header-mobile-redirect .control-group:last-child {
|
||||
margin-left: 87px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#mappings .input-medium {width: 85px;}
|
||||
|
||||
#space-group .top-actions .pull-right {
|
||||
float: none;
|
||||
clear: both;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.window-tree {width: 356px;}
|
||||
|
||||
/* Footer */
|
||||
|
||||
footer {
|
||||
width: 724px;
|
||||
height: 50px;
|
||||
margin-top: -50px;
|
||||
}
|
||||
|
||||
footer p {
|
||||
margin-left: 186px;
|
||||
float: none;
|
||||
}
|
||||
|
||||
footer > ul {
|
||||
margin-left: 173px;
|
||||
float: none;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.navbar-fixed-top, .navbar-fixed-bottom {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
/* Topbar */
|
||||
|
||||
.navbar-fixed-top {
|
||||
position: absolute;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.navbar-fixed-top .navbar-inner {padding: 0;}
|
||||
|
||||
.nav-collapse {
|
||||
height: auto;
|
||||
overflow: visible;
|
||||
clear: none;
|
||||
}
|
||||
|
||||
.nav-collapse .nav {margin-bottom: 0;}
|
||||
|
||||
.navbar .nav-collapse .nav.pull-right {float: right;}
|
||||
|
||||
.navbar #settings {display: none;}
|
||||
|
||||
.nav-collapse .nav > li {float: left;}
|
||||
|
||||
.nav-collapse .nav > li > a {
|
||||
font-weight: normal;
|
||||
padding: 9px 10px 11px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Topbar dropdown */
|
||||
|
||||
.navbar .dropdown-menu {display: none;}
|
||||
|
||||
.open .dropdown-menu,
|
||||
.open .dropdown-menu:before,
|
||||
.open .dropdown-menu:after {display: block;}
|
||||
|
||||
.open .dropdown-menu {
|
||||
background-color: #FFFFFF;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
border-radius: 5px 5px 5px 5px;
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
margin: 1px 0 0;
|
||||
padding: 4px 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.nav-collapse .dropdown-menu a {
|
||||
color: #333333;
|
||||
line-height: 18px;
|
||||
padding: 3px 15px;
|
||||
border-radius: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.nav-collapse .dropdown-menu li > a:hover,
|
||||
.nav-collapse .dropdown-menu .active > a,
|
||||
.nav-collapse .dropdown-menu .active > a:hover {
|
||||
background-color: #0088CC;
|
||||
color: #FFFFFF;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* */
|
||||
|
||||
#wrapper {margin-top: 40px;}
|
||||
|
||||
aside.span3,
|
||||
#actions-bg,
|
||||
#container-right-bg {display: none;}
|
||||
|
||||
.row {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
#container-right {
|
||||
box-shadow: none;
|
||||
background: none;
|
||||
padding-bottom: 74px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.span9 {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.input-large,
|
||||
.input-xlarge,
|
||||
.input-xxlarge,
|
||||
input[class*="span"],
|
||||
select[class*="span"],
|
||||
textarea[class*="span"],
|
||||
.uneditable-input {width: 80%;}
|
||||
|
||||
footer {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
footer > ul,
|
||||
footer >p {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
footer ul li:first-child {
|
||||
padding-left: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
.margin-top {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
div#httpProviderError {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 20px;
|
||||
}
|
||||
|
||||
div#loading {
|
||||
color: #868686;
|
||||
font-size: 14px;
|
||||
position: fixed;
|
||||
left: 0px;
|
||||
bottom: 5px;
|
||||
}
|
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 999 B |
Before Width: | Height: | Size: 935 B |
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 80 B |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 4.4 KiB |
|
@ -1,50 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="en" data-ng-app="keycloak">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Keycloak</title>
|
||||
|
||||
<link href="lib/bootstrap/css/bootstrap.css" rel="stylesheet">
|
||||
<link href="lib/bootstrap/css/bootstrap-responsive.css" rel="stylesheet">
|
||||
<link href="css/admin.css" rel="stylesheet">
|
||||
<link href="css/admin-responsive.css" rel="stylesheet">
|
||||
|
||||
<link href="css/styles.css" rel="stylesheet">
|
||||
|
||||
<script src="lib/jquery/jquery-1.10.2.js"></script>
|
||||
|
||||
<script src="lib/angular/angular.js"></script>
|
||||
<script src="lib/angular/angular-resource.js"></script>
|
||||
<script src="lib/angular/ui-bootstrap-tpls-0.4.0.js"></script>
|
||||
|
||||
<script src="js/app.js"></script>
|
||||
<script src="js/controllers.js"></script>
|
||||
<script src="js/loaders.js"></script>
|
||||
<script src="js/services.js"></script>
|
||||
</head>
|
||||
|
||||
<body data-ng-controller="GlobalCtrl">
|
||||
|
||||
<div class="alert-container" data-ng-show="notification" data-ng-click="notification = null">
|
||||
<div class="alert alert-{{notification.type}}">{{notification.message}}</div>
|
||||
</div>
|
||||
|
||||
<div id="wrap">
|
||||
<div data-ng-include data-src="'partials/menu.html'"></div>
|
||||
|
||||
<div data-ng-view id="view" data-ng-hide="httpProviderError"></div>
|
||||
|
||||
<div id="httpProviderError" data-ng-show="httpProviderError">
|
||||
<button class="btn btn-danger" data-ng-click="httpProviderError=null">
|
||||
<strong>Error</strong> {{httpProviderError}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="loading">
|
||||
<i class="icon-spinner icon-spin"></i> Loading...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,290 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var module = angular.module('keycloak', [ 'keycloak.services', 'keycloak.loaders', 'keycloak.controllers', 'ui.bootstrap' ]);
|
||||
var resourceRequests = 0;
|
||||
|
||||
module.config([ '$routeProvider', function($routeProvider) {
|
||||
|
||||
$routeProvider.when('/create/application', {
|
||||
templateUrl : 'partials/application-detail.html',
|
||||
resolve : {
|
||||
application : function(ApplicationLoader) {
|
||||
return {};
|
||||
},
|
||||
realms : function(RealmListLoader) {
|
||||
return RealmListLoader();
|
||||
}
|
||||
},
|
||||
controller : 'ApplicationDetailCtrl'
|
||||
}).when('/applications/:application', {
|
||||
templateUrl : 'partials/application-detail.html',
|
||||
resolve : {
|
||||
application : function(ApplicationLoader) {
|
||||
return ApplicationLoader();
|
||||
},
|
||||
realms : function(RealmListLoader) {
|
||||
return RealmListLoader();
|
||||
}
|
||||
},
|
||||
controller : 'ApplicationDetailCtrl'
|
||||
}).when('/applications', {
|
||||
templateUrl : 'partials/application-list.html',
|
||||
resolve : {
|
||||
applications : function(ApplicationListLoader) {
|
||||
return ApplicationListLoader();
|
||||
}
|
||||
},
|
||||
controller : 'ApplicationListCtrl'
|
||||
})
|
||||
|
||||
.when('/create/realm', {
|
||||
templateUrl : 'partials/realm-detail.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
controller : 'RealmDetailCtrl'
|
||||
}).when('/realms/:realm', {
|
||||
templateUrl : 'partials/realm-detail.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
}
|
||||
},
|
||||
controller : 'RealmDetailCtrl'
|
||||
}).when('/realms', {
|
||||
templateUrl : 'partials/realm-list.html',
|
||||
controller : 'RealmListCtrl'
|
||||
})
|
||||
|
||||
.when('/create/user/:realm', {
|
||||
templateUrl : 'partials/user-detail.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
user : function() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
controller : 'UserDetailCtrl'
|
||||
}).when('/realms/:realm/users/:user', {
|
||||
templateUrl : 'partials/user-detail.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
user : function(UserLoader) {
|
||||
return UserLoader();
|
||||
}
|
||||
},
|
||||
controller : 'UserDetailCtrl'
|
||||
}).when('/realms/:realm/users', {
|
||||
templateUrl : 'partials/user-list.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
users : function(UserListLoader) {
|
||||
return UserListLoader();
|
||||
}
|
||||
},
|
||||
controller : 'UserListCtrl'
|
||||
})
|
||||
|
||||
.when('/realms/:realm/roles', {
|
||||
templateUrl : 'partials/role-mapping.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
application : function() {
|
||||
return null;
|
||||
},
|
||||
users : function() {
|
||||
return null;
|
||||
},
|
||||
role : function() {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
controller : 'RoleMappingCtrl'
|
||||
}).when('/realms/:realm/roles/:role', {
|
||||
templateUrl : 'partials/role-mapping.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
application : function() {
|
||||
return null;
|
||||
},
|
||||
role : function($route) {
|
||||
return $route.current.params.role;
|
||||
},
|
||||
users : function(RoleMappingLoader) {
|
||||
return RoleMappingLoader();
|
||||
}
|
||||
},
|
||||
controller : 'RoleMappingCtrl'
|
||||
})
|
||||
|
||||
.when('/applications/:application/roles', {
|
||||
templateUrl : 'partials/role-mapping.html',
|
||||
resolve : {
|
||||
realm : function(ApplicationLoader) {
|
||||
return ApplicationLoader();
|
||||
},
|
||||
users : function() {
|
||||
return null;
|
||||
},
|
||||
role : function() {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
controller : 'RoleMappingCtrl'
|
||||
}).when('/applications/:application/roles/:role', {
|
||||
templateUrl : 'partials/role-mapping.html',
|
||||
resolve : {
|
||||
realm : function(ApplicationLoader) {
|
||||
return ApplicationLoader();
|
||||
},
|
||||
role : function($route) {
|
||||
return $route.current.params.role;
|
||||
},
|
||||
users : function(RoleMappingLoader) {
|
||||
return RoleMappingLoader();
|
||||
}
|
||||
},
|
||||
controller : 'RoleMappingCtrl'
|
||||
})
|
||||
|
||||
.otherwise({
|
||||
templateUrl : 'partials/home.html'
|
||||
});
|
||||
} ]);
|
||||
|
||||
module.config(function($httpProvider) {
|
||||
$httpProvider.responseInterceptors.push('errorInterceptor');
|
||||
|
||||
var spinnerFunction = function(data, headersGetter) {
|
||||
if (resourceRequests == 0) {
|
||||
$('#loading').show();
|
||||
}
|
||||
resourceRequests++;
|
||||
return data;
|
||||
};
|
||||
$httpProvider.defaults.transformRequest.push(spinnerFunction);
|
||||
|
||||
$httpProvider.responseInterceptors.push('spinnerInterceptor');
|
||||
|
||||
});
|
||||
|
||||
module.factory('errorInterceptor', function($q, $window, $rootScope, $location) {
|
||||
return function(promise) {
|
||||
return promise.then(function(response) {
|
||||
$rootScope.httpProviderError = null;
|
||||
return response;
|
||||
}, function(response) {
|
||||
$rootScope.httpProviderError = response.status;
|
||||
return $q.reject(response);
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.factory('spinnerInterceptor', function($q, $window, $rootScope, $location) {
|
||||
return function(promise) {
|
||||
return promise.then(function(response) {
|
||||
resourceRequests--;
|
||||
if (resourceRequests == 0) {
|
||||
$('#loading').hide();
|
||||
}
|
||||
return response;
|
||||
}, function(response) {
|
||||
resourceRequests--;
|
||||
if (resourceRequests == 0) {
|
||||
$('#loading').hide();
|
||||
}
|
||||
|
||||
return $q.reject(response);
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.directive('kcInput', function() {
|
||||
var d = {
|
||||
scope : true,
|
||||
replace : false,
|
||||
link : function(scope, element, attrs) {
|
||||
var form = element.closest('form');
|
||||
var label = element.children('label');
|
||||
var input = element.children('input');
|
||||
|
||||
var id = form.attr('name') + '.' + input.attr('name');
|
||||
|
||||
element.attr('class', 'control-group');
|
||||
|
||||
label.attr('class', 'control-label');
|
||||
label.attr('for', id);
|
||||
|
||||
input.wrap('<div class="controls"/>');
|
||||
input.attr('id', id);
|
||||
|
||||
if (!input.attr('placeHolder')) {
|
||||
input.attr('placeHolder', label.text());
|
||||
}
|
||||
|
||||
if (input.attr('required')) {
|
||||
label.append(' <span class="required">*</span>');
|
||||
}
|
||||
}
|
||||
};
|
||||
return d;
|
||||
});
|
||||
|
||||
module.directive('kcEnter', function() {
|
||||
return function(scope, element, attrs) {
|
||||
element.bind("keydown keypress", function(event) {
|
||||
if (event.which === 13) {
|
||||
scope.$apply(function() {
|
||||
scope.$eval(attrs.kcEnter);
|
||||
});
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.filter('remove', function() {
|
||||
return function(input, remove, attribute) {
|
||||
if (!input || !remove) {
|
||||
return input;
|
||||
}
|
||||
|
||||
var out = [];
|
||||
for ( var i = 0; i < input.length; i++) {
|
||||
var e = input[i];
|
||||
|
||||
for (var j = 0; j < remove.length; j++) {
|
||||
if (attribute) {
|
||||
if (remove[j][attribute] == e[attribute]) {
|
||||
e = null;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (remove[j] == e) {
|
||||
e = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (e != null) {
|
||||
out.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
};
|
||||
});
|
|
@ -1,348 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var module = angular.module('keycloak.controllers', [ 'keycloak.services' ]);
|
||||
|
||||
module.controller('GlobalCtrl', function($scope, Auth, $location, Notifications) {
|
||||
$scope.addMessage = function() {
|
||||
Notifications.success("test");
|
||||
};
|
||||
|
||||
$scope.auth = Auth;
|
||||
|
||||
$scope.$watch(function() {
|
||||
return $location.path();
|
||||
}, function() {
|
||||
$scope.path = $location.path().substring(1).split("/");
|
||||
});
|
||||
});
|
||||
|
||||
module.controller('ApplicationListCtrl', function($scope, Application) {
|
||||
$scope.applications = Application.query();
|
||||
});
|
||||
|
||||
module.controller('ApplicationDetailCtrl', function($scope, application, Application, realms, $location, $window, Dialog,
|
||||
Notifications) {
|
||||
$scope.application = angular.copy(application);
|
||||
$scope.realms = realms;
|
||||
|
||||
$scope.create = !application.id;
|
||||
$scope.changed = $scope.create;
|
||||
|
||||
$scope.$watch('application', function() {
|
||||
if (!angular.equals($scope.application, application)) {
|
||||
$scope.changed = true;
|
||||
}
|
||||
}, true);
|
||||
|
||||
$scope.addRole = function() {
|
||||
if ($scope.newRole) {
|
||||
if ($scope.application.roles) {
|
||||
for ( var i = 0; i < $scope.application.roles.length; i++) {
|
||||
if ($scope.application.roles[i] == $scope.newRole) {
|
||||
Notifications.warn("Role already exists");
|
||||
$scope.newRole = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$scope.application.roles) {
|
||||
$scope.application.roles = [];
|
||||
}
|
||||
|
||||
$scope.application.roles.push($scope.newRole);
|
||||
$scope.newRole = null;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.removeRole = function(role) {
|
||||
Dialog.confirmDelete(role, 'role', function() {
|
||||
var i = $scope.application.roles.indexOf(role);
|
||||
if (i > -1) {
|
||||
$scope.application.roles.splice(i, 1);
|
||||
}
|
||||
|
||||
if ($scope.application.initialRoles) {
|
||||
$scope.removeInitialRole(role);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addInitialRole = function() {
|
||||
if ($scope.newInitialRole) {
|
||||
if (!$scope.application.initialRoles) {
|
||||
$scope.application.initialRoles = [];
|
||||
}
|
||||
|
||||
$scope.application.initialRoles.push($scope.newInitialRole);
|
||||
$scope.newInitialRole = null;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.removeInitialRole = function(role) {
|
||||
var i = $scope.application.initialRoles.indexOf(role);
|
||||
if (i > -1) {
|
||||
$scope.application.initialRoles.splice(i, 1);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.save = function() {
|
||||
if ($scope.applicationForm.$valid) {
|
||||
if ($scope.create) {
|
||||
Application.save($scope.application, function(data, headers) {
|
||||
var l = headers().location;
|
||||
var id = l.substring(l.lastIndexOf("/") + 1);
|
||||
$location.url("/applications/" + id);
|
||||
Notifications.success("Created application");
|
||||
});
|
||||
} else {
|
||||
Application.update($scope.application, function() {
|
||||
$scope.changed = false;
|
||||
application = angular.copy($scope.application);
|
||||
Notifications.success("Saved changes to the application");
|
||||
});
|
||||
}
|
||||
} else {
|
||||
$scope.applicationForm.showErrors = true;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.application = angular.copy(application);
|
||||
$scope.changed = false;
|
||||
$scope.applicationForm.showErrors = false;
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$location.url("/applications");
|
||||
};
|
||||
|
||||
$scope.remove = function() {
|
||||
Dialog.confirmDelete($scope.application.name, 'application', function() {
|
||||
$scope.application.$remove(function() {
|
||||
$location.url("/applications");
|
||||
Notifications.success("Deleted application");
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
module.controller('RealmListCtrl', function($scope, Realm) {
|
||||
$scope.realms = Realm.query();
|
||||
});
|
||||
|
||||
module.controller('RealmDetailCtrl', function($scope, Realm, realm, $location, Dialog, Notifications) {
|
||||
$scope.realm = angular.copy(realm);
|
||||
$scope.create = !realm.name;
|
||||
|
||||
$scope.changed = $scope.create;
|
||||
|
||||
$scope.$watch('realm', function() {
|
||||
if (!angular.equals($scope.realm, realm)) {
|
||||
$scope.changed = true;
|
||||
}
|
||||
}, true);
|
||||
|
||||
$scope.addRole = function() {
|
||||
if ($scope.newRole) {
|
||||
if ($scope.realm.roles) {
|
||||
for ( var i = 0; i < $scope.realm.roles.length; i++) {
|
||||
if ($scope.realm.roles[i] == $scope.newRole) {
|
||||
Notifications.warn("Role already exists");
|
||||
$scope.newRole = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$scope.realm.roles) {
|
||||
$scope.realm.roles = [];
|
||||
}
|
||||
|
||||
$scope.realm.roles.push($scope.newRole);
|
||||
$scope.newRole = null;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.removeRole = function(role) {
|
||||
Dialog.confirmDelete(role, 'role', function() {
|
||||
var i = $scope.realm.roles.indexOf(role);
|
||||
if (i > -1) {
|
||||
$scope.realm.roles.splice(i, 1);
|
||||
}
|
||||
|
||||
if ($scope.realm.initialRoles) {
|
||||
$scope.removeInitialRole(role);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addInitialRole = function() {
|
||||
if ($scope.newInitialRole) {
|
||||
if (!$scope.realm.initialRoles) {
|
||||
$scope.realm.initialRoles = [];
|
||||
}
|
||||
|
||||
$scope.realm.initialRoles.push($scope.newInitialRole);
|
||||
$scope.newInitialRole = null;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.removeInitialRole = function(role) {
|
||||
var i = $scope.realm.initialRoles.indexOf(role);
|
||||
if (i > -1) {
|
||||
$scope.realm.initialRoles.splice(i, 1);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.save = function() {
|
||||
if ($scope.realmForm.$valid) {
|
||||
if ($scope.create) {
|
||||
Realm.save($scope.realm, function(data, headers) {
|
||||
var l = headers().location;
|
||||
var id = l.substring(l.lastIndexOf("/") + 1);
|
||||
$location.url("/realms/" + id);
|
||||
Notifications.success("Created realm");
|
||||
});
|
||||
} else {
|
||||
Realm.update($scope.realm, function() {
|
||||
$scope.changed = false;
|
||||
realm = angular.copy($scope.realm);
|
||||
Notifications.success("Saved changes to realm");
|
||||
});
|
||||
}
|
||||
} else {
|
||||
$scope.realmForm.showErrors = true;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.realm = angular.copy(realm);
|
||||
$scope.changed = false;
|
||||
$scope.realmForm.showErrors = false;
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$location.url("/realms");
|
||||
};
|
||||
|
||||
$scope.remove = function() {
|
||||
Dialog.confirmDelete($scope.realm.name, 'realm', function() {
|
||||
Realm.remove($scope.realm, function() {
|
||||
$location.url("/realms");
|
||||
Notifications.success("Deleted realm");
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
module.controller('UserListCtrl', function($scope, realm, users) {
|
||||
$scope.realm = realm;
|
||||
$scope.users = users;
|
||||
});
|
||||
|
||||
module.controller('UserDetailCtrl', function($scope, realm, user, User, $location, Dialog, Notifications) {
|
||||
$scope.realm = realm;
|
||||
$scope.user = angular.copy(user);
|
||||
$scope.create = !user.userId;
|
||||
|
||||
$scope.changed = $scope.create;
|
||||
|
||||
$scope.$watch('user', function() {
|
||||
if (!angular.equals($scope.user, user)) {
|
||||
$scope.changed = true;
|
||||
}
|
||||
}, true);
|
||||
|
||||
$scope.save = function() {
|
||||
if ($scope.userForm.$valid) {
|
||||
User.save({
|
||||
realm : realm.id,
|
||||
}, $scope.user, function() {
|
||||
$scope.changed = false;
|
||||
user = angular.copy($scope.user);
|
||||
|
||||
if ($scope.create) {
|
||||
$location.url("/realms/" + realm.id + "/users/" + $scope.user.userId);
|
||||
Notifications.success("Created user");
|
||||
} else {
|
||||
Notifications.success("Saved changes to user");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$scope.userForm.showErrors = true;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.user = angular.copy(user);
|
||||
$scope.changed = false;
|
||||
$scope.userForm.showErrors = false;
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$location.url("/realms/" + realm.id + "/users");
|
||||
};
|
||||
|
||||
$scope.remove = function() {
|
||||
Dialog.confirmDelete($scope.user.userId, 'user', function() {
|
||||
$scope.user.$remove({
|
||||
realm : realm.id,
|
||||
userId : $scope.user.userId
|
||||
}, function() {
|
||||
$location.url("/realms/" + realm.id + "/users");
|
||||
Notifications.success("Deleted user");
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.controller('RoleMappingCtrl', function($scope, realm, User, users, role, RoleMapping, Notifications) {
|
||||
$scope.realm = realm;
|
||||
$scope.realmId = realm.realm || realm.id;
|
||||
$scope.allUsers = User.query({ realm : $scope.realmId });
|
||||
$scope.users = users;
|
||||
$scope.role = role;
|
||||
|
||||
$scope.addUser = function() {
|
||||
var user = $scope.newUser;
|
||||
$scope.newUser = null;
|
||||
|
||||
for ( var i = 0; i < $scope.allUsers.length; i++) {
|
||||
if ($scope.allUsers[i].userId == user) {
|
||||
user = $scope.allUsers[i];
|
||||
RoleMapping.save({
|
||||
realm : $scope.realmId,
|
||||
role : role
|
||||
}, user, function() {
|
||||
$scope.users = RoleMapping.query({
|
||||
realm : $scope.realmId,
|
||||
role : role
|
||||
});
|
||||
Notifications.success("Added role mapping for user");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$scope.removeUser = function(userId) {
|
||||
for (var i = 0; i < $scope.users.length; i++) {
|
||||
var user = $scope.users[i];
|
||||
if ($scope.users[i].userId == userId) {
|
||||
RoleMapping.delete({
|
||||
realm : $scope.realmId,
|
||||
role : role
|
||||
}, user, function() {
|
||||
$scope.users = RoleMapping.query({
|
||||
realm : $scope.realmId,
|
||||
role : role
|
||||
});
|
||||
|
||||
Notifications.success("Removed role mapping for user");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,84 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var module = angular.module('keycloak.loaders', [ 'keycloak.services', 'ngResource' ]);
|
||||
|
||||
module.factory('Loader', function($q) {
|
||||
var loader = {};
|
||||
loader.get = function(service, id) {
|
||||
return function() {
|
||||
var i = id && id();
|
||||
var delay = $q.defer();
|
||||
service.get(i, function(entry) {
|
||||
delay.resolve(entry);
|
||||
}, function() {
|
||||
delay.reject('Unable to fetch ' + i);
|
||||
});
|
||||
return delay.promise;
|
||||
};
|
||||
}
|
||||
loader.query = function(service, id) {
|
||||
return function() {
|
||||
var i = id && id();
|
||||
var delay = $q.defer();
|
||||
service.query(i, function(entry) {
|
||||
delay.resolve(entry);
|
||||
}, function() {
|
||||
delay.reject('Unable to fetch ' + i);
|
||||
});
|
||||
return delay.promise;
|
||||
};
|
||||
}
|
||||
return loader;
|
||||
});
|
||||
|
||||
module.factory('ApplicationListLoader', function(Loader, Application, $q) {
|
||||
return Loader.query(Application);
|
||||
});
|
||||
|
||||
module.factory('ApplicationLoader', function(Loader, Application, $route, $q) {
|
||||
return Loader.get(Application, function() {
|
||||
return {
|
||||
id : $route.current.params.application
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('RealmListLoader', function(Loader, Realm, $q) {
|
||||
return Loader.query(Realm);
|
||||
});
|
||||
|
||||
module.factory('RealmLoader', function(Loader, Realm, $route, $q) {
|
||||
return Loader.get(Realm, function() {
|
||||
return {
|
||||
id : $route.current.params.realm
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('UserListLoader', function(Loader, User, $route, $q) {
|
||||
return Loader.query(User, function() {
|
||||
return {
|
||||
realm : $route.current.params.realm
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('UserLoader', function(Loader, User, $route, $q) {
|
||||
return Loader.get(User, function() {
|
||||
return {
|
||||
realm : $route.current.params.realm,
|
||||
userId : $route.current.params.user
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('RoleMappingLoader', function(Loader, RoleMapping, $route, $q) {
|
||||
var realm = $route.current.params.realm || $route.current.params.application;
|
||||
|
||||
return Loader.query(RoleMapping, function() {
|
||||
return {
|
||||
realm : realm,
|
||||
role : $route.current.params.role
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,131 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var module = angular.module('keycloak.services', [ 'ngResource' ]);
|
||||
|
||||
module.service('Auth', function($resource, $http, $location, $routeParams) {
|
||||
var auth = {
|
||||
loggedIn : true
|
||||
};
|
||||
auth.user = {
|
||||
userId : 'test',
|
||||
displayName : 'Test User'
|
||||
};
|
||||
return auth;
|
||||
});
|
||||
|
||||
module.service('Dialog', function($dialog) {
|
||||
var dialog = {};
|
||||
dialog.confirmDelete = function(name, type, success) {
|
||||
var title = 'Delete ' + name;
|
||||
var msg = 'Are you sure you want to permanently delete this ' + type + '?';
|
||||
var btns = [ {
|
||||
result : 'cancel',
|
||||
label : 'Cancel'
|
||||
}, {
|
||||
result : 'ok',
|
||||
label : 'Delete this ' + type,
|
||||
cssClass : 'btn-primary'
|
||||
} ];
|
||||
|
||||
$dialog.messageBox(title, msg, btns).open().then(function(result) {
|
||||
if (result == "ok") {
|
||||
success();
|
||||
}
|
||||
});
|
||||
}
|
||||
return dialog
|
||||
});
|
||||
|
||||
module.factory('Notifications', function($rootScope, $timeout) {
|
||||
var notifications = {};
|
||||
|
||||
var scheduled = null;
|
||||
var schedulePop = function() {
|
||||
if (scheduled) {
|
||||
$timeout.cancel(scheduled);
|
||||
}
|
||||
|
||||
scheduled = $timeout(function() {
|
||||
$rootScope.notification = null;
|
||||
scheduled = null;
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
if (!$rootScope.notifications) {
|
||||
$rootScope.notifications = [];
|
||||
}
|
||||
|
||||
notifications.message = function(type, message) {
|
||||
$rootScope.notification = {
|
||||
type : type,
|
||||
message : message
|
||||
};
|
||||
|
||||
schedulePop();
|
||||
}
|
||||
|
||||
notifications.info = function(message) {
|
||||
notifications.message("info", message);
|
||||
};
|
||||
|
||||
notifications.success = function(message) {
|
||||
notifications.message("success", message);
|
||||
};
|
||||
|
||||
notifications.error = function(message) {
|
||||
notifications.message("error", message);
|
||||
};
|
||||
|
||||
notifications.warn = function(message) {
|
||||
notifications.message("warn", message);
|
||||
};
|
||||
|
||||
return notifications;
|
||||
});
|
||||
|
||||
module.factory('Application', function($resource) {
|
||||
return $resource('/keycloak-server/ui/api/applications/:id', {
|
||||
id : '@id'
|
||||
}, {
|
||||
update : {
|
||||
method : 'PUT'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('Provider', function($resource) {
|
||||
return $resource('/ejs-identity/api/admin/providers');
|
||||
});
|
||||
|
||||
module.factory('Realm', function($resource) {
|
||||
return $resource('/keycloak-server/ui/api/realms/:id', {
|
||||
id : '@id'
|
||||
}, {
|
||||
update : {
|
||||
method : 'PUT'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('RoleMapping', function($resource) {
|
||||
return $resource('/keycloak-server/ui/api/roles/:realm/:role/:userId', {
|
||||
realm : '@realm',
|
||||
role : '@role',
|
||||
userId : '@userId'
|
||||
}, {
|
||||
save : {
|
||||
method : 'PUT'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('User', function($resource) {
|
||||
return $resource('/keycloak-server/ui/api/realms/:realm/users/:userId', {
|
||||
realm : '@realm',
|
||||
userId : '@userId'
|
||||
}, {
|
||||
save : {
|
||||
method : 'PUT'
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,175 +0,0 @@
|
|||
/**
|
||||
* @license AngularJS v1.0.7
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {
|
||||
'use strict';
|
||||
|
||||
var directive = {};
|
||||
|
||||
directive.dropdownToggle =
|
||||
['$document', '$location', '$window',
|
||||
function ($document, $location, $window) {
|
||||
var openElement = null, close;
|
||||
return {
|
||||
restrict: 'C',
|
||||
link: function(scope, element, attrs) {
|
||||
scope.$watch(function dropdownTogglePathWatch(){return $location.path();}, function dropdownTogglePathWatchAction() {
|
||||
close && close();
|
||||
});
|
||||
|
||||
element.parent().bind('click', function(event) {
|
||||
close && close();
|
||||
});
|
||||
|
||||
element.bind('click', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
var iWasOpen = false;
|
||||
|
||||
if (openElement) {
|
||||
iWasOpen = openElement === element;
|
||||
close();
|
||||
}
|
||||
|
||||
if (!iWasOpen){
|
||||
element.parent().addClass('open');
|
||||
openElement = element;
|
||||
|
||||
close = function (event) {
|
||||
event && event.preventDefault();
|
||||
event && event.stopPropagation();
|
||||
$document.unbind('click', close);
|
||||
element.parent().removeClass('open');
|
||||
close = null;
|
||||
openElement = null;
|
||||
}
|
||||
|
||||
$document.bind('click', close);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
|
||||
directive.tabbable = function() {
|
||||
return {
|
||||
restrict: 'C',
|
||||
compile: function(element) {
|
||||
var navTabs = angular.element('<ul class="nav nav-tabs"></ul>'),
|
||||
tabContent = angular.element('<div class="tab-content"></div>');
|
||||
|
||||
tabContent.append(element.contents());
|
||||
element.append(navTabs).append(tabContent);
|
||||
},
|
||||
controller: ['$scope', '$element', function($scope, $element) {
|
||||
var navTabs = $element.contents().eq(0),
|
||||
ngModel = $element.controller('ngModel') || {},
|
||||
tabs = [],
|
||||
selectedTab;
|
||||
|
||||
ngModel.$render = function() {
|
||||
var $viewValue = this.$viewValue;
|
||||
|
||||
if (selectedTab ? (selectedTab.value != $viewValue) : $viewValue) {
|
||||
if(selectedTab) {
|
||||
selectedTab.paneElement.removeClass('active');
|
||||
selectedTab.tabElement.removeClass('active');
|
||||
selectedTab = null;
|
||||
}
|
||||
if($viewValue) {
|
||||
for(var i = 0, ii = tabs.length; i < ii; i++) {
|
||||
if ($viewValue == tabs[i].value) {
|
||||
selectedTab = tabs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (selectedTab) {
|
||||
selectedTab.paneElement.addClass('active');
|
||||
selectedTab.tabElement.addClass('active');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
this.addPane = function(element, attr) {
|
||||
var li = angular.element('<li><a href></a></li>'),
|
||||
a = li.find('a'),
|
||||
tab = {
|
||||
paneElement: element,
|
||||
paneAttrs: attr,
|
||||
tabElement: li
|
||||
};
|
||||
|
||||
tabs.push(tab);
|
||||
|
||||
attr.$observe('value', update)();
|
||||
attr.$observe('title', function(){ update(); a.text(tab.title); })();
|
||||
|
||||
function update() {
|
||||
tab.title = attr.title;
|
||||
tab.value = attr.value || attr.title;
|
||||
if (!ngModel.$setViewValue && (!ngModel.$viewValue || tab == selectedTab)) {
|
||||
// we are not part of angular
|
||||
ngModel.$viewValue = tab.value;
|
||||
}
|
||||
ngModel.$render();
|
||||
}
|
||||
|
||||
navTabs.append(li);
|
||||
li.bind('click', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (ngModel.$setViewValue) {
|
||||
$scope.$apply(function() {
|
||||
ngModel.$setViewValue(tab.value);
|
||||
ngModel.$render();
|
||||
});
|
||||
} else {
|
||||
// we are not part of angular
|
||||
ngModel.$viewValue = tab.value;
|
||||
ngModel.$render();
|
||||
}
|
||||
});
|
||||
|
||||
return function() {
|
||||
tab.tabElement.remove();
|
||||
for(var i = 0, ii = tabs.length; i < ii; i++ ) {
|
||||
if (tab == tabs[i]) {
|
||||
tabs.splice(i, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}]
|
||||
};
|
||||
};
|
||||
|
||||
directive.table = function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function(scope, element, attrs) {
|
||||
element[0].className = 'table table-bordered table-striped code-table';
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
directive.tabPane = function() {
|
||||
return {
|
||||
require: '^tabbable',
|
||||
restrict: 'C',
|
||||
link: function(scope, element, attrs, tabsCtrl) {
|
||||
element.bind('$remove', tabsCtrl.addPane(element, attrs));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
angular.module('bootstrap', []).directive(directive);
|
||||
|
||||
|
||||
})(window, window.angular);
|
|
@ -1,185 +0,0 @@
|
|||
/**
|
||||
* @license AngularJS v1.0.7
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name ngCookies
|
||||
*/
|
||||
|
||||
|
||||
angular.module('ngCookies', ['ng']).
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ngCookies.$cookies
|
||||
* @requires $browser
|
||||
*
|
||||
* @description
|
||||
* Provides read/write access to browser's cookies.
|
||||
*
|
||||
* Only a simple Object is exposed and by adding or removing properties to/from
|
||||
* this object, new cookies are created/deleted at the end of current $eval.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<script>
|
||||
function ExampleController($cookies) {
|
||||
// Retrieving a cookie
|
||||
var favoriteCookie = $cookies.myFavorite;
|
||||
// Setting a cookie
|
||||
$cookies.myFavorite = 'oatmeal';
|
||||
}
|
||||
</script>
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
*/
|
||||
factory('$cookies', ['$rootScope', '$browser', function ($rootScope, $browser) {
|
||||
var cookies = {},
|
||||
lastCookies = {},
|
||||
lastBrowserCookies,
|
||||
runEval = false,
|
||||
copy = angular.copy,
|
||||
isUndefined = angular.isUndefined;
|
||||
|
||||
//creates a poller fn that copies all cookies from the $browser to service & inits the service
|
||||
$browser.addPollFn(function() {
|
||||
var currentCookies = $browser.cookies();
|
||||
if (lastBrowserCookies != currentCookies) { //relies on browser.cookies() impl
|
||||
lastBrowserCookies = currentCookies;
|
||||
copy(currentCookies, lastCookies);
|
||||
copy(currentCookies, cookies);
|
||||
if (runEval) $rootScope.$apply();
|
||||
}
|
||||
})();
|
||||
|
||||
runEval = true;
|
||||
|
||||
//at the end of each eval, push cookies
|
||||
//TODO: this should happen before the "delayed" watches fire, because if some cookies are not
|
||||
// strings or browser refuses to store some cookies, we update the model in the push fn.
|
||||
$rootScope.$watch(push);
|
||||
|
||||
return cookies;
|
||||
|
||||
|
||||
/**
|
||||
* Pushes all the cookies from the service to the browser and verifies if all cookies were stored.
|
||||
*/
|
||||
function push() {
|
||||
var name,
|
||||
value,
|
||||
browserCookies,
|
||||
updated;
|
||||
|
||||
//delete any cookies deleted in $cookies
|
||||
for (name in lastCookies) {
|
||||
if (isUndefined(cookies[name])) {
|
||||
$browser.cookies(name, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
//update all cookies updated in $cookies
|
||||
for(name in cookies) {
|
||||
value = cookies[name];
|
||||
if (!angular.isString(value)) {
|
||||
if (angular.isDefined(lastCookies[name])) {
|
||||
cookies[name] = lastCookies[name];
|
||||
} else {
|
||||
delete cookies[name];
|
||||
}
|
||||
} else if (value !== lastCookies[name]) {
|
||||
$browser.cookies(name, value);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
//verify what was actually stored
|
||||
if (updated){
|
||||
updated = false;
|
||||
browserCookies = $browser.cookies();
|
||||
|
||||
for (name in cookies) {
|
||||
if (cookies[name] !== browserCookies[name]) {
|
||||
//delete or reset all cookies that the browser dropped from $cookies
|
||||
if (isUndefined(browserCookies[name])) {
|
||||
delete cookies[name];
|
||||
} else {
|
||||
cookies[name] = browserCookies[name];
|
||||
}
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}]).
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ngCookies.$cookieStore
|
||||
* @requires $cookies
|
||||
*
|
||||
* @description
|
||||
* Provides a key-value (string-object) storage, that is backed by session cookies.
|
||||
* Objects put or retrieved from this storage are automatically serialized or
|
||||
* deserialized by angular's toJson/fromJson.
|
||||
* @example
|
||||
*/
|
||||
factory('$cookieStore', ['$cookies', function($cookies) {
|
||||
|
||||
return {
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ngCookies.$cookieStore#get
|
||||
* @methodOf ngCookies.$cookieStore
|
||||
*
|
||||
* @description
|
||||
* Returns the value of given cookie key
|
||||
*
|
||||
* @param {string} key Id to use for lookup.
|
||||
* @returns {Object} Deserialized cookie value.
|
||||
*/
|
||||
get: function(key) {
|
||||
var value = $cookies[key];
|
||||
return value ? angular.fromJson(value) : value;
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ngCookies.$cookieStore#put
|
||||
* @methodOf ngCookies.$cookieStore
|
||||
*
|
||||
* @description
|
||||
* Sets a value for given cookie key
|
||||
*
|
||||
* @param {string} key Id for the `value`.
|
||||
* @param {Object} value Value to be stored.
|
||||
*/
|
||||
put: function(key, value) {
|
||||
$cookies[key] = angular.toJson(value);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ngCookies.$cookieStore#remove
|
||||
* @methodOf ngCookies.$cookieStore
|
||||
*
|
||||
* @description
|
||||
* Remove given cookie
|
||||
*
|
||||
* @param {string} key Id of the key-value pair to delete.
|
||||
*/
|
||||
remove: function(key) {
|
||||
delete $cookies[key];
|
||||
}
|
||||
};
|
||||
|
||||
}]);
|
||||
|
||||
|
||||
})(window, window.angular);
|
|
@ -1,277 +0,0 @@
|
|||
/**
|
||||
* @license AngularJS v1.0.7
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
(
|
||||
|
||||
/**
|
||||
* @ngdoc interface
|
||||
* @name angular.Module
|
||||
* @description
|
||||
*
|
||||
* Interface for configuring angular {@link angular.module modules}.
|
||||
*/
|
||||
|
||||
function setupModuleLoader(window) {
|
||||
|
||||
function ensure(obj, name, factory) {
|
||||
return obj[name] || (obj[name] = factory());
|
||||
}
|
||||
|
||||
return ensure(ensure(window, 'angular', Object), 'module', function() {
|
||||
/** @type {Object.<string, angular.Module>} */
|
||||
var modules = {};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.module
|
||||
* @description
|
||||
*
|
||||
* The `angular.module` is a global place for creating and registering Angular modules. All
|
||||
* modules (angular core or 3rd party) that should be available to an application must be
|
||||
* registered using this mechanism.
|
||||
*
|
||||
*
|
||||
* # Module
|
||||
*
|
||||
* A module is a collocation of services, directives, filters, and configuration information. Module
|
||||
* is used to configure the {@link AUTO.$injector $injector}.
|
||||
*
|
||||
* <pre>
|
||||
* // Create a new module
|
||||
* var myModule = angular.module('myModule', []);
|
||||
*
|
||||
* // register a new service
|
||||
* myModule.value('appName', 'MyCoolApp');
|
||||
*
|
||||
* // configure existing services inside initialization blocks.
|
||||
* myModule.config(function($locationProvider) {
|
||||
'use strict';
|
||||
* // Configure existing providers
|
||||
* $locationProvider.hashPrefix('!');
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* Then you can create an injector and load your modules like this:
|
||||
*
|
||||
* <pre>
|
||||
* var injector = angular.injector(['ng', 'MyModule'])
|
||||
* </pre>
|
||||
*
|
||||
* However it's more likely that you'll just use
|
||||
* {@link ng.directive:ngApp ngApp} or
|
||||
* {@link angular.bootstrap} to simplify this process for you.
|
||||
*
|
||||
* @param {!string} name The name of the module to create or retrieve.
|
||||
* @param {Array.<string>=} requires If specified then new module is being created. If unspecified then the
|
||||
* the module is being retrieved for further configuration.
|
||||
* @param {Function} configFn Optional configuration function for the module. Same as
|
||||
* {@link angular.Module#config Module#config()}.
|
||||
* @returns {module} new module with the {@link angular.Module} api.
|
||||
*/
|
||||
return function module(name, requires, configFn) {
|
||||
if (requires && modules.hasOwnProperty(name)) {
|
||||
modules[name] = null;
|
||||
}
|
||||
return ensure(modules, name, function() {
|
||||
if (!requires) {
|
||||
throw Error('No module: ' + name);
|
||||
}
|
||||
|
||||
/** @type {!Array.<Array.<*>>} */
|
||||
var invokeQueue = [];
|
||||
|
||||
/** @type {!Array.<Function>} */
|
||||
var runBlocks = [];
|
||||
|
||||
var config = invokeLater('$injector', 'invoke');
|
||||
|
||||
/** @type {angular.Module} */
|
||||
var moduleInstance = {
|
||||
// Private state
|
||||
_invokeQueue: invokeQueue,
|
||||
_runBlocks: runBlocks,
|
||||
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name angular.Module#requires
|
||||
* @propertyOf angular.Module
|
||||
* @returns {Array.<string>} List of module names which must be loaded before this module.
|
||||
* @description
|
||||
* Holds the list of modules which the injector will load before the current module is loaded.
|
||||
*/
|
||||
requires: requires,
|
||||
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name angular.Module#name
|
||||
* @propertyOf angular.Module
|
||||
* @returns {string} Name of the module.
|
||||
* @description
|
||||
*/
|
||||
name: name,
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#provider
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name service name
|
||||
* @param {Function} providerType Construction function for creating new instance of the service.
|
||||
* @description
|
||||
* See {@link AUTO.$provide#provider $provide.provider()}.
|
||||
*/
|
||||
provider: invokeLater('$provide', 'provider'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#factory
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name service name
|
||||
* @param {Function} providerFunction Function for creating new instance of the service.
|
||||
* @description
|
||||
* See {@link AUTO.$provide#factory $provide.factory()}.
|
||||
*/
|
||||
factory: invokeLater('$provide', 'factory'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#service
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name service name
|
||||
* @param {Function} constructor A constructor function that will be instantiated.
|
||||
* @description
|
||||
* See {@link AUTO.$provide#service $provide.service()}.
|
||||
*/
|
||||
service: invokeLater('$provide', 'service'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#value
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name service name
|
||||
* @param {*} object Service instance object.
|
||||
* @description
|
||||
* See {@link AUTO.$provide#value $provide.value()}.
|
||||
*/
|
||||
value: invokeLater('$provide', 'value'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#constant
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name constant name
|
||||
* @param {*} object Constant value.
|
||||
* @description
|
||||
* Because the constant are fixed, they get applied before other provide methods.
|
||||
* See {@link AUTO.$provide#constant $provide.constant()}.
|
||||
*/
|
||||
constant: invokeLater('$provide', 'constant', 'unshift'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#filter
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name Filter name.
|
||||
* @param {Function} filterFactory Factory function for creating new instance of filter.
|
||||
* @description
|
||||
* See {@link ng.$filterProvider#register $filterProvider.register()}.
|
||||
*/
|
||||
filter: invokeLater('$filterProvider', 'register'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#controller
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name Controller name.
|
||||
* @param {Function} constructor Controller constructor function.
|
||||
* @description
|
||||
* See {@link ng.$controllerProvider#register $controllerProvider.register()}.
|
||||
*/
|
||||
controller: invokeLater('$controllerProvider', 'register'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#directive
|
||||
* @methodOf angular.Module
|
||||
* @param {string} name directive name
|
||||
* @param {Function} directiveFactory Factory function for creating new instance of
|
||||
* directives.
|
||||
* @description
|
||||
* See {@link ng.$compileProvider#directive $compileProvider.directive()}.
|
||||
*/
|
||||
directive: invokeLater('$compileProvider', 'directive'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#config
|
||||
* @methodOf angular.Module
|
||||
* @param {Function} configFn Execute this function on module load. Useful for service
|
||||
* configuration.
|
||||
* @description
|
||||
* Use this method to register work which needs to be performed on module loading.
|
||||
*/
|
||||
config: config,
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#run
|
||||
* @methodOf angular.Module
|
||||
* @param {Function} initializationFn Execute this function after injector creation.
|
||||
* Useful for application initialization.
|
||||
* @description
|
||||
* Use this method to register work which should be performed when the injector is done
|
||||
* loading all modules.
|
||||
*/
|
||||
run: function(block) {
|
||||
runBlocks.push(block);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
if (configFn) {
|
||||
config(configFn);
|
||||
}
|
||||
|
||||
return moduleInstance;
|
||||
|
||||
/**
|
||||
* @param {string} provider
|
||||
* @param {string} method
|
||||
* @param {String=} insertMethod
|
||||
* @returns {angular.Module}
|
||||
*/
|
||||
function invokeLater(provider, method, insertMethod) {
|
||||
return function() {
|
||||
invokeQueue[insertMethod || 'push']([provider, method, arguments]);
|
||||
return moduleInstance;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
)(window);
|
||||
|
||||
/**
|
||||
* Closure compiler type information
|
||||
*
|
||||
* @typedef { {
|
||||
* requires: !Array.<string>,
|
||||
* invokeQueue: !Array.<Array.<*>>,
|
||||
*
|
||||
* service: function(string, Function):angular.Module,
|
||||
* factory: function(string, Function):angular.Module,
|
||||
* value: function(string, *):angular.Module,
|
||||
*
|
||||
* filter: function(string, Function):angular.Module,
|
||||
*
|
||||
* init: function(Function):angular.Module
|
||||
* } }
|
||||
*/
|
||||
angular.Module;
|
||||
|
|
@ -1,457 +0,0 @@
|
|||
/**
|
||||
* @license AngularJS v1.0.7
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name ngResource
|
||||
* @description
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ngResource.$resource
|
||||
* @requires $http
|
||||
*
|
||||
* @description
|
||||
* A factory which creates a resource object that lets you interact with
|
||||
* [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
|
||||
*
|
||||
* The returned resource object has action methods which provide high-level behaviors without
|
||||
* the need to interact with the low level {@link ng.$http $http} service.
|
||||
*
|
||||
* # Installation
|
||||
* To use $resource make sure you have included the `angular-resource.js` that comes in Angular
|
||||
* package. You can also find this file on Google CDN, bower as well as at
|
||||
* {@link http://code.angularjs.org/ code.angularjs.org}.
|
||||
*
|
||||
* Finally load the module in your application:
|
||||
*
|
||||
* angular.module('app', ['ngResource']);
|
||||
*
|
||||
* and you are ready to get started!
|
||||
*
|
||||
* @param {string} url A parameterized URL template with parameters prefixed by `:` as in
|
||||
* `/user/:username`. If you are using a URL with a port number (e.g.
|
||||
* `http://example.com:8080/api`), you'll need to escape the colon character before the port
|
||||
* number, like this: `$resource('http://example.com\\:8080/api')`.
|
||||
*
|
||||
* @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
|
||||
* `actions` methods.
|
||||
*
|
||||
* Each key value in the parameter object is first bound to url template if present and then any
|
||||
* excess keys are appended to the url search query after the `?`.
|
||||
*
|
||||
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
|
||||
* URL `/path/greet?salutation=Hello`.
|
||||
*
|
||||
* If the parameter value is prefixed with `@` then the value of that parameter is extracted from
|
||||
* the data object (useful for non-GET operations).
|
||||
*
|
||||
* @param {Object.<Object>=} actions Hash with declaration of custom action that should extend the
|
||||
* default set of resource actions. The declaration should be created in the following format:
|
||||
*
|
||||
* {action1: {method:?, params:?, isArray:?},
|
||||
* action2: {method:?, params:?, isArray:?},
|
||||
* ...}
|
||||
*
|
||||
* Where:
|
||||
*
|
||||
* - `action` – {string} – The name of action. This name becomes the name of the method on your
|
||||
* resource object.
|
||||
* - `method` – {string} – HTTP request method. Valid methods are: `GET`, `POST`, `PUT`, `DELETE`,
|
||||
* and `JSONP`
|
||||
* - `params` – {object=} – Optional set of pre-bound parameters for this action.
|
||||
* - isArray – {boolean=} – If true then the returned object for this action is an array, see
|
||||
* `returns` section.
|
||||
*
|
||||
* @returns {Object} A resource "class" object with methods for the default set of resource actions
|
||||
* optionally extended with custom `actions`. The default set contains these actions:
|
||||
*
|
||||
* { 'get': {method:'GET'},
|
||||
* 'save': {method:'POST'},
|
||||
* 'query': {method:'GET', isArray:true},
|
||||
* 'remove': {method:'DELETE'},
|
||||
* 'delete': {method:'DELETE'} };
|
||||
*
|
||||
* Calling these methods invoke an {@link ng.$http} with the specified http method,
|
||||
* destination and parameters. When the data is returned from the server then the object is an
|
||||
* instance of the resource class. The actions `save`, `remove` and `delete` are available on it
|
||||
* as methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
|
||||
* read, update, delete) on server-side data like this:
|
||||
* <pre>
|
||||
var User = $resource('/user/:userId', {userId:'@id'});
|
||||
var user = User.get({userId:123}, function() {
|
||||
user.abc = true;
|
||||
user.$save();
|
||||
});
|
||||
</pre>
|
||||
*
|
||||
* It is important to realize that invoking a $resource object method immediately returns an
|
||||
* empty reference (object or array depending on `isArray`). Once the data is returned from the
|
||||
* server the existing reference is populated with the actual data. This is a useful trick since
|
||||
* usually the resource is assigned to a model which is then rendered by the view. Having an empty
|
||||
* object results in no rendering, once the data arrives from the server then the object is
|
||||
* populated with the data and the view automatically re-renders itself showing the new data. This
|
||||
* means that in most case one never has to write a callback function for the action methods.
|
||||
*
|
||||
* The action methods on the class object or instance object can be invoked with the following
|
||||
* parameters:
|
||||
*
|
||||
* - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])`
|
||||
* - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
|
||||
* - non-GET instance actions: `instance.$action([parameters], [success], [error])`
|
||||
*
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* # Credit card resource
|
||||
*
|
||||
* <pre>
|
||||
// Define CreditCard class
|
||||
var CreditCard = $resource('/user/:userId/card/:cardId',
|
||||
{userId:123, cardId:'@id'}, {
|
||||
charge: {method:'POST', params:{charge:true}}
|
||||
});
|
||||
|
||||
// We can retrieve a collection from the server
|
||||
var cards = CreditCard.query(function() {
|
||||
// GET: /user/123/card
|
||||
// server returns: [ {id:456, number:'1234', name:'Smith'} ];
|
||||
|
||||
var card = cards[0];
|
||||
// each item is an instance of CreditCard
|
||||
expect(card instanceof CreditCard).toEqual(true);
|
||||
card.name = "J. Smith";
|
||||
// non GET methods are mapped onto the instances
|
||||
card.$save();
|
||||
// POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
|
||||
// server returns: {id:456, number:'1234', name: 'J. Smith'};
|
||||
|
||||
// our custom method is mapped as well.
|
||||
card.$charge({amount:9.99});
|
||||
// POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
|
||||
});
|
||||
|
||||
// we can create an instance as well
|
||||
var newCard = new CreditCard({number:'0123'});
|
||||
newCard.name = "Mike Smith";
|
||||
newCard.$save();
|
||||
// POST: /user/123/card {number:'0123', name:'Mike Smith'}
|
||||
// server returns: {id:789, number:'01234', name: 'Mike Smith'};
|
||||
expect(newCard.id).toEqual(789);
|
||||
* </pre>
|
||||
*
|
||||
* The object returned from this function execution is a resource "class" which has "static" method
|
||||
* for each action in the definition.
|
||||
*
|
||||
* Calling these methods invoke `$http` on the `url` template with the given `method` and `params`.
|
||||
* When the data is returned from the server then the object is an instance of the resource type and
|
||||
* all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
|
||||
* operations (create, read, update, delete) on server-side data.
|
||||
|
||||
<pre>
|
||||
var User = $resource('/user/:userId', {userId:'@id'});
|
||||
var user = User.get({userId:123}, function() {
|
||||
user.abc = true;
|
||||
user.$save();
|
||||
});
|
||||
</pre>
|
||||
*
|
||||
* It's worth noting that the success callback for `get`, `query` and other method gets passed
|
||||
* in the response that came from the server as well as $http header getter function, so one
|
||||
* could rewrite the above example and get access to http headers as:
|
||||
*
|
||||
<pre>
|
||||
var User = $resource('/user/:userId', {userId:'@id'});
|
||||
User.get({userId:123}, function(u, getResponseHeaders){
|
||||
u.abc = true;
|
||||
u.$save(function(u, putResponseHeaders) {
|
||||
//u => saved user object
|
||||
//putResponseHeaders => $http header getter
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
* # Buzz client
|
||||
|
||||
Let's look at what a buzz client created with the `$resource` service looks like:
|
||||
<doc:example>
|
||||
<doc:source jsfiddle="false">
|
||||
<script>
|
||||
function BuzzController($resource) {
|
||||
this.userId = 'googlebuzz';
|
||||
this.Activity = $resource(
|
||||
'https://www.googleapis.com/buzz/v1/activities/:userId/:visibility/:activityId/:comments',
|
||||
{alt:'json', callback:'JSON_CALLBACK'},
|
||||
{get:{method:'JSONP', params:{visibility:'@self'}}, replies: {method:'JSONP', params:{visibility:'@self', comments:'@comments'}}}
|
||||
);
|
||||
}
|
||||
|
||||
BuzzController.prototype = {
|
||||
fetch: function() {
|
||||
this.activities = this.Activity.get({userId:this.userId});
|
||||
},
|
||||
expandReplies: function(activity) {
|
||||
activity.replies = this.Activity.replies({userId:this.userId, activityId:activity.id});
|
||||
}
|
||||
};
|
||||
BuzzController.$inject = ['$resource'];
|
||||
</script>
|
||||
|
||||
<div ng-controller="BuzzController">
|
||||
<input ng-model="userId"/>
|
||||
<button ng-click="fetch()">fetch</button>
|
||||
<hr/>
|
||||
<div ng-repeat="item in activities.data.items">
|
||||
<h1 style="font-size: 15px;">
|
||||
<img src="{{item.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
|
||||
<a href="{{item.actor.profileUrl}}">{{item.actor.name}}</a>
|
||||
<a href ng-click="expandReplies(item)" style="float: right;">Expand replies: {{item.links.replies[0].count}}</a>
|
||||
</h1>
|
||||
{{item.object.content | html}}
|
||||
<div ng-repeat="reply in item.replies.data.items" style="margin-left: 20px;">
|
||||
<img src="{{reply.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
|
||||
<a href="{{reply.actor.profileUrl}}">{{reply.actor.name}}</a>: {{reply.content | html}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angular.module('ngResource', ['ng']).
|
||||
factory('$resource', ['$http', '$parse', function($http, $parse) {
|
||||
var DEFAULT_ACTIONS = {
|
||||
'get': {method:'GET'},
|
||||
'save': {method:'POST'},
|
||||
'query': {method:'GET', isArray:true},
|
||||
'remove': {method:'DELETE'},
|
||||
'delete': {method:'DELETE'}
|
||||
};
|
||||
var noop = angular.noop,
|
||||
forEach = angular.forEach,
|
||||
extend = angular.extend,
|
||||
copy = angular.copy,
|
||||
isFunction = angular.isFunction,
|
||||
getter = function(obj, path) {
|
||||
return $parse(path)(obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* We need our custom method because encodeURIComponent is too aggressive and doesn't follow
|
||||
* http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
|
||||
* segments:
|
||||
* segment = *pchar
|
||||
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
* pct-encoded = "%" HEXDIG HEXDIG
|
||||
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||
* / "*" / "+" / "," / ";" / "="
|
||||
*/
|
||||
function encodeUriSegment(val) {
|
||||
return encodeUriQuery(val, true).
|
||||
replace(/%26/gi, '&').
|
||||
replace(/%3D/gi, '=').
|
||||
replace(/%2B/gi, '+');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is intended for encoding *key* or *value* parts of query component. We need a custom
|
||||
* method becuase encodeURIComponent is too agressive and encodes stuff that doesn't have to be
|
||||
* encoded per http://tools.ietf.org/html/rfc3986:
|
||||
* query = *( pchar / "/" / "?" )
|
||||
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||
* pct-encoded = "%" HEXDIG HEXDIG
|
||||
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||
* / "*" / "+" / "," / ";" / "="
|
||||
*/
|
||||
function encodeUriQuery(val, pctEncodeSpaces) {
|
||||
return encodeURIComponent(val).
|
||||
replace(/%40/gi, '@').
|
||||
replace(/%3A/gi, ':').
|
||||
replace(/%24/g, '$').
|
||||
replace(/%2C/gi, ',').
|
||||
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
|
||||
}
|
||||
|
||||
function Route(template, defaults) {
|
||||
this.template = template = template + '#';
|
||||
this.defaults = defaults || {};
|
||||
var urlParams = this.urlParams = {};
|
||||
forEach(template.split(/\W/), function(param){
|
||||
if (param && (new RegExp("(^|[^\\\\]):" + param + "\\W").test(template))) {
|
||||
urlParams[param] = true;
|
||||
}
|
||||
});
|
||||
this.template = template.replace(/\\:/g, ':');
|
||||
}
|
||||
|
||||
Route.prototype = {
|
||||
url: function(params) {
|
||||
var self = this,
|
||||
url = this.template,
|
||||
val,
|
||||
encodedVal;
|
||||
|
||||
params = params || {};
|
||||
forEach(this.urlParams, function(_, urlParam){
|
||||
val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
|
||||
if (angular.isDefined(val) && val !== null) {
|
||||
encodedVal = encodeUriSegment(val);
|
||||
url = url.replace(new RegExp(":" + urlParam + "(\\W)", "g"), encodedVal + "$1");
|
||||
} else {
|
||||
url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W)", "g"), function(match,
|
||||
leadingSlashes, tail) {
|
||||
if (tail.charAt(0) == '/') {
|
||||
return tail;
|
||||
} else {
|
||||
return leadingSlashes + tail;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
url = url.replace(/\/?#$/, '');
|
||||
var query = [];
|
||||
forEach(params, function(value, key){
|
||||
if (!self.urlParams[key]) {
|
||||
query.push(encodeUriQuery(key) + '=' + encodeUriQuery(value));
|
||||
}
|
||||
});
|
||||
query.sort();
|
||||
url = url.replace(/\/*$/, '');
|
||||
return url + (query.length ? '?' + query.join('&') : '');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function ResourceFactory(url, paramDefaults, actions) {
|
||||
var route = new Route(url);
|
||||
|
||||
actions = extend({}, DEFAULT_ACTIONS, actions);
|
||||
|
||||
function extractParams(data, actionParams){
|
||||
var ids = {};
|
||||
actionParams = extend({}, paramDefaults, actionParams);
|
||||
forEach(actionParams, function(value, key){
|
||||
ids[key] = value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value;
|
||||
});
|
||||
return ids;
|
||||
}
|
||||
|
||||
function Resource(value){
|
||||
copy(value || {}, this);
|
||||
}
|
||||
|
||||
forEach(actions, function(action, name) {
|
||||
action.method = angular.uppercase(action.method);
|
||||
var hasBody = action.method == 'POST' || action.method == 'PUT' || action.method == 'PATCH';
|
||||
Resource[name] = function(a1, a2, a3, a4) {
|
||||
var params = {};
|
||||
var data;
|
||||
var success = noop;
|
||||
var error = null;
|
||||
switch(arguments.length) {
|
||||
case 4:
|
||||
error = a4;
|
||||
success = a3;
|
||||
//fallthrough
|
||||
case 3:
|
||||
case 2:
|
||||
if (isFunction(a2)) {
|
||||
if (isFunction(a1)) {
|
||||
success = a1;
|
||||
error = a2;
|
||||
break;
|
||||
}
|
||||
|
||||
success = a2;
|
||||
error = a3;
|
||||
//fallthrough
|
||||
} else {
|
||||
params = a1;
|
||||
data = a2;
|
||||
success = a3;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
if (isFunction(a1)) success = a1;
|
||||
else if (hasBody) data = a1;
|
||||
else params = a1;
|
||||
break;
|
||||
case 0: break;
|
||||
default:
|
||||
throw "Expected between 0-4 arguments [params, data, success, error], got " +
|
||||
arguments.length + " arguments.";
|
||||
}
|
||||
|
||||
var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data));
|
||||
$http({
|
||||
method: action.method,
|
||||
url: route.url(extend({}, extractParams(data, action.params || {}), params)),
|
||||
data: data
|
||||
}).then(function(response) {
|
||||
var data = response.data;
|
||||
|
||||
if (data) {
|
||||
if (action.isArray) {
|
||||
value.length = 0;
|
||||
forEach(data, function(item) {
|
||||
value.push(new Resource(item));
|
||||
});
|
||||
} else {
|
||||
copy(data, value);
|
||||
}
|
||||
}
|
||||
(success||noop)(value, response.headers);
|
||||
}, error);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
|
||||
Resource.prototype['$' + name] = function(a1, a2, a3) {
|
||||
var params = extractParams(this),
|
||||
success = noop,
|
||||
error;
|
||||
|
||||
switch(arguments.length) {
|
||||
case 3: params = a1; success = a2; error = a3; break;
|
||||
case 2:
|
||||
case 1:
|
||||
if (isFunction(a1)) {
|
||||
success = a1;
|
||||
error = a2;
|
||||
} else {
|
||||
params = a1;
|
||||
success = a2 || noop;
|
||||
}
|
||||
case 0: break;
|
||||
default:
|
||||
throw "Expected between 1-3 arguments [params, success, error], got " +
|
||||
arguments.length + " arguments.";
|
||||
}
|
||||
var data = hasBody ? this : undefined;
|
||||
Resource[name].call(this, params, data, success, error);
|
||||
};
|
||||
});
|
||||
|
||||
Resource.bind = function(additionalParamDefaults){
|
||||
return ResourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
|
||||
};
|
||||
|
||||
return Resource;
|
||||
}
|
||||
|
||||
return ResourceFactory;
|
||||
}]);
|
||||
|
||||
|
||||
})(window, window.angular);
|
|
@ -1,537 +0,0 @@
|
|||
/**
|
||||
* @license AngularJS v1.0.7
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name ngSanitize
|
||||
* @description
|
||||
*/
|
||||
|
||||
/*
|
||||
* HTML Parser By Misko Hevery (misko@hevery.com)
|
||||
* based on: HTML Parser By John Resig (ejohn.org)
|
||||
* Original code by Erik Arvidsson, Mozilla Public License
|
||||
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
|
||||
*
|
||||
* // Use like so:
|
||||
* htmlParser(htmlString, {
|
||||
* start: function(tag, attrs, unary) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* });
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name ngSanitize.$sanitize
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* The input is sanitized by parsing the html into tokens. All safe tokens (from a whitelist) are
|
||||
* then serialized back to properly escaped html string. This means that no unsafe input can make
|
||||
* it into the returned string, however, since our parser is more strict than a typical browser
|
||||
* parser, it's possible that some obscure input, which would be recognized as valid HTML by a
|
||||
* browser, won't make it through the sanitizer.
|
||||
*
|
||||
* @param {string} html Html input.
|
||||
* @returns {string} Sanitized html.
|
||||
*
|
||||
* @example
|
||||
<doc:example module="ngSanitize">
|
||||
<doc:source>
|
||||
<script>
|
||||
function Ctrl($scope) {
|
||||
$scope.snippet =
|
||||
'<p style="color:blue">an html\n' +
|
||||
'<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
|
||||
'snippet</p>';
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl">
|
||||
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Filter</td>
|
||||
<td>Source</td>
|
||||
<td>Rendered</td>
|
||||
</tr>
|
||||
<tr id="html-filter">
|
||||
<td>html filter</td>
|
||||
<td>
|
||||
<pre><div ng-bind-html="snippet"><br/></div></pre>
|
||||
</td>
|
||||
<td>
|
||||
<div ng-bind-html="snippet"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="escaped-html">
|
||||
<td>no filter</td>
|
||||
<td><pre><div ng-bind="snippet"><br/></div></pre></td>
|
||||
<td><div ng-bind="snippet"></div></td>
|
||||
</tr>
|
||||
<tr id="html-unsafe-filter">
|
||||
<td>unsafe html filter</td>
|
||||
<td><pre><div ng-bind-html-unsafe="snippet"><br/></div></pre></td>
|
||||
<td><div ng-bind-html-unsafe="snippet"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should sanitize the html snippet ', function() {
|
||||
expect(using('#html-filter').element('div').html()).
|
||||
toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
|
||||
});
|
||||
|
||||
it('should escape snippet without any filter', function() {
|
||||
expect(using('#escaped-html').element('div').html()).
|
||||
toBe("<p style=\"color:blue\">an html\n" +
|
||||
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
|
||||
"snippet</p>");
|
||||
});
|
||||
|
||||
it('should inline raw snippet if filtered as unsafe', function() {
|
||||
expect(using('#html-unsafe-filter').element("div").html()).
|
||||
toBe("<p style=\"color:blue\">an html\n" +
|
||||
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
|
||||
"snippet</p>");
|
||||
});
|
||||
|
||||
it('should update', function() {
|
||||
input('snippet').enter('new <b>text</b>');
|
||||
expect(using('#html-filter').binding('snippet')).toBe('new <b>text</b>');
|
||||
expect(using('#escaped-html').element('div').html()).toBe("new <b>text</b>");
|
||||
expect(using('#html-unsafe-filter').binding("snippet")).toBe('new <b>text</b>');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
var $sanitize = function(html) {
|
||||
var buf = [];
|
||||
htmlParser(html, htmlSanitizeWriter(buf));
|
||||
return buf.join('');
|
||||
};
|
||||
|
||||
|
||||
// Regular Expressions for parsing tags and attributes
|
||||
var START_TAG_REGEXP = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,
|
||||
END_TAG_REGEXP = /^<\s*\/\s*([\w:-]+)[^>]*>/,
|
||||
ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,
|
||||
BEGIN_TAG_REGEXP = /^</,
|
||||
BEGING_END_TAGE_REGEXP = /^<\s*\//,
|
||||
COMMENT_REGEXP = /<!--(.*?)-->/g,
|
||||
CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
|
||||
URI_REGEXP = /^((ftp|https?):\/\/|mailto:|#)/,
|
||||
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; // Match everything outside of normal chars and " (quote character)
|
||||
|
||||
|
||||
// Good source of info about elements and attributes
|
||||
// http://dev.w3.org/html5/spec/Overview.html#semantics
|
||||
// http://simon.html5.org/html-elements
|
||||
|
||||
// Safe Void Elements - HTML5
|
||||
// http://dev.w3.org/html5/spec/Overview.html#void-elements
|
||||
var voidElements = makeMap("area,br,col,hr,img,wbr");
|
||||
|
||||
// Elements that you can, intentionally, leave open (and which close themselves)
|
||||
// http://dev.w3.org/html5/spec/Overview.html#optional-tags
|
||||
var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
|
||||
optionalEndTagInlineElements = makeMap("rp,rt"),
|
||||
optionalEndTagElements = angular.extend({}, optionalEndTagInlineElements, optionalEndTagBlockElements);
|
||||
|
||||
// Safe Block Elements - HTML5
|
||||
var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article,aside," +
|
||||
"blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6," +
|
||||
"header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul"));
|
||||
|
||||
// Inline Elements - HTML5
|
||||
var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b,bdi,bdo," +
|
||||
"big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small," +
|
||||
"span,strike,strong,sub,sup,time,tt,u,var"));
|
||||
|
||||
|
||||
// Special Elements (can contain anything)
|
||||
var specialElements = makeMap("script,style");
|
||||
|
||||
var validElements = angular.extend({}, voidElements, blockElements, inlineElements, optionalEndTagElements);
|
||||
|
||||
//Attributes that have href and hence need to be sanitized
|
||||
var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap");
|
||||
var validAttrs = angular.extend({}, uriAttrs, makeMap(
|
||||
'abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+
|
||||
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+
|
||||
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+
|
||||
'scope,scrolling,shape,span,start,summary,target,title,type,'+
|
||||
'valign,value,vspace,width'));
|
||||
|
||||
function makeMap(str) {
|
||||
var obj = {}, items = str.split(','), i;
|
||||
for (i = 0; i < items.length; i++) obj[items[i]] = true;
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @example
|
||||
* htmlParser(htmlString, {
|
||||
* start: function(tag, attrs, unary) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* });
|
||||
*
|
||||
* @param {string} html string
|
||||
* @param {object} handler
|
||||
*/
|
||||
function htmlParser( html, handler ) {
|
||||
var index, chars, match, stack = [], last = html;
|
||||
stack.last = function() { return stack[ stack.length - 1 ]; };
|
||||
|
||||
while ( html ) {
|
||||
chars = true;
|
||||
|
||||
// Make sure we're not in a script or style element
|
||||
if ( !stack.last() || !specialElements[ stack.last() ] ) {
|
||||
|
||||
// Comment
|
||||
if ( html.indexOf("<!--") === 0 ) {
|
||||
index = html.indexOf("-->");
|
||||
|
||||
if ( index >= 0 ) {
|
||||
if (handler.comment) handler.comment( html.substring( 4, index ) );
|
||||
html = html.substring( index + 3 );
|
||||
chars = false;
|
||||
}
|
||||
|
||||
// end tag
|
||||
} else if ( BEGING_END_TAGE_REGEXP.test(html) ) {
|
||||
match = html.match( END_TAG_REGEXP );
|
||||
|
||||
if ( match ) {
|
||||
html = html.substring( match[0].length );
|
||||
match[0].replace( END_TAG_REGEXP, parseEndTag );
|
||||
chars = false;
|
||||
}
|
||||
|
||||
// start tag
|
||||
} else if ( BEGIN_TAG_REGEXP.test(html) ) {
|
||||
match = html.match( START_TAG_REGEXP );
|
||||
|
||||
if ( match ) {
|
||||
html = html.substring( match[0].length );
|
||||
match[0].replace( START_TAG_REGEXP, parseStartTag );
|
||||
chars = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( chars ) {
|
||||
index = html.indexOf("<");
|
||||
|
||||
var text = index < 0 ? html : html.substring( 0, index );
|
||||
html = index < 0 ? "" : html.substring( index );
|
||||
|
||||
if (handler.chars) handler.chars( decodeEntities(text) );
|
||||
}
|
||||
|
||||
} else {
|
||||
html = html.replace(new RegExp("(.*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'), function(all, text){
|
||||
text = text.
|
||||
replace(COMMENT_REGEXP, "$1").
|
||||
replace(CDATA_REGEXP, "$1");
|
||||
|
||||
if (handler.chars) handler.chars( decodeEntities(text) );
|
||||
|
||||
return "";
|
||||
});
|
||||
|
||||
parseEndTag( "", stack.last() );
|
||||
}
|
||||
|
||||
if ( html == last ) {
|
||||
throw "Parse Error: " + html;
|
||||
}
|
||||
last = html;
|
||||
}
|
||||
|
||||
// Clean up any remaining tags
|
||||
parseEndTag();
|
||||
|
||||
function parseStartTag( tag, tagName, rest, unary ) {
|
||||
tagName = angular.lowercase(tagName);
|
||||
if ( blockElements[ tagName ] ) {
|
||||
while ( stack.last() && inlineElements[ stack.last() ] ) {
|
||||
parseEndTag( "", stack.last() );
|
||||
}
|
||||
}
|
||||
|
||||
if ( optionalEndTagElements[ tagName ] && stack.last() == tagName ) {
|
||||
parseEndTag( "", tagName );
|
||||
}
|
||||
|
||||
unary = voidElements[ tagName ] || !!unary;
|
||||
|
||||
if ( !unary )
|
||||
stack.push( tagName );
|
||||
|
||||
var attrs = {};
|
||||
|
||||
rest.replace(ATTR_REGEXP, function(match, name, doubleQuotedValue, singleQoutedValue, unqoutedValue) {
|
||||
var value = doubleQuotedValue
|
||||
|| singleQoutedValue
|
||||
|| unqoutedValue
|
||||
|| '';
|
||||
|
||||
attrs[name] = decodeEntities(value);
|
||||
});
|
||||
if (handler.start) handler.start( tagName, attrs, unary );
|
||||
}
|
||||
|
||||
function parseEndTag( tag, tagName ) {
|
||||
var pos = 0, i;
|
||||
tagName = angular.lowercase(tagName);
|
||||
if ( tagName )
|
||||
// Find the closest opened tag of the same type
|
||||
for ( pos = stack.length - 1; pos >= 0; pos-- )
|
||||
if ( stack[ pos ] == tagName )
|
||||
break;
|
||||
|
||||
if ( pos >= 0 ) {
|
||||
// Close all the open elements, up the stack
|
||||
for ( i = stack.length - 1; i >= pos; i-- )
|
||||
if (handler.end) handler.end( stack[ i ] );
|
||||
|
||||
// Remove the open elements from the stack
|
||||
stack.length = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* decodes all entities into regular string
|
||||
* @param value
|
||||
* @returns {string} A string with decoded entities.
|
||||
*/
|
||||
var hiddenPre=document.createElement("pre");
|
||||
function decodeEntities(value) {
|
||||
hiddenPre.innerHTML=value.replace(/</g,"<");
|
||||
return hiddenPre.innerText || hiddenPre.textContent || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes all potentially dangerous characters, so that the
|
||||
* resulting string can be safely inserted into attribute or
|
||||
* element text.
|
||||
* @param value
|
||||
* @returns escaped text
|
||||
*/
|
||||
function encodeEntities(value) {
|
||||
return value.
|
||||
replace(/&/g, '&').
|
||||
replace(NON_ALPHANUMERIC_REGEXP, function(value){
|
||||
return '&#' + value.charCodeAt(0) + ';';
|
||||
}).
|
||||
replace(/</g, '<').
|
||||
replace(/>/g, '>');
|
||||
}
|
||||
|
||||
/**
|
||||
* create an HTML/XML writer which writes to buffer
|
||||
* @param {Array} buf use buf.jain('') to get out sanitized html string
|
||||
* @returns {object} in the form of {
|
||||
* start: function(tag, attrs, unary) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* }
|
||||
*/
|
||||
function htmlSanitizeWriter(buf){
|
||||
var ignore = false;
|
||||
var out = angular.bind(buf, buf.push);
|
||||
return {
|
||||
start: function(tag, attrs, unary){
|
||||
tag = angular.lowercase(tag);
|
||||
if (!ignore && specialElements[tag]) {
|
||||
ignore = tag;
|
||||
}
|
||||
if (!ignore && validElements[tag] == true) {
|
||||
out('<');
|
||||
out(tag);
|
||||
angular.forEach(attrs, function(value, key){
|
||||
var lkey=angular.lowercase(key);
|
||||
if (validAttrs[lkey]==true && (uriAttrs[lkey]!==true || value.match(URI_REGEXP))) {
|
||||
out(' ');
|
||||
out(key);
|
||||
out('="');
|
||||
out(encodeEntities(value));
|
||||
out('"');
|
||||
}
|
||||
});
|
||||
out(unary ? '/>' : '>');
|
||||
}
|
||||
},
|
||||
end: function(tag){
|
||||
tag = angular.lowercase(tag);
|
||||
if (!ignore && validElements[tag] == true) {
|
||||
out('</');
|
||||
out(tag);
|
||||
out('>');
|
||||
}
|
||||
if (tag == ignore) {
|
||||
ignore = false;
|
||||
}
|
||||
},
|
||||
chars: function(chars){
|
||||
if (!ignore) {
|
||||
out(encodeEntities(chars));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// define ngSanitize module and register $sanitize service
|
||||
angular.module('ngSanitize', []).value('$sanitize', $sanitize);
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngSanitize.directive:ngBindHtml
|
||||
*
|
||||
* @description
|
||||
* Creates a binding that will sanitize the result of evaluating the `expression` with the
|
||||
* {@link ngSanitize.$sanitize $sanitize} service and innerHTML the result into the current element.
|
||||
*
|
||||
* See {@link ngSanitize.$sanitize $sanitize} docs for examples.
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
|
||||
*/
|
||||
angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($sanitize) {
|
||||
return function(scope, element, attr) {
|
||||
element.addClass('ng-binding').data('$binding', attr.ngBindHtml);
|
||||
scope.$watch(attr.ngBindHtml, function ngBindHtmlWatchAction(value) {
|
||||
value = $sanitize(value);
|
||||
element.html(value || '');
|
||||
});
|
||||
};
|
||||
}]);
|
||||
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name ngSanitize.filter:linky
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
|
||||
* plain email address links.
|
||||
*
|
||||
* @param {string} text Input text.
|
||||
* @returns {string} Html-linkified text.
|
||||
*
|
||||
* @usage
|
||||
<span ng-bind-html="linky_expression | linky"></span>
|
||||
*
|
||||
* @example
|
||||
<doc:example module="ngSanitize">
|
||||
<doc:source>
|
||||
<script>
|
||||
function Ctrl($scope) {
|
||||
$scope.snippet =
|
||||
'Pretty text with some links:\n'+
|
||||
'http://angularjs.org/,\n'+
|
||||
'mailto:us@somewhere.org,\n'+
|
||||
'another@somewhere.org,\n'+
|
||||
'and one more: ftp://127.0.0.1/.';
|
||||
}
|
||||
</script>
|
||||
<div ng-controller="Ctrl">
|
||||
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Filter</td>
|
||||
<td>Source</td>
|
||||
<td>Rendered</td>
|
||||
</tr>
|
||||
<tr id="linky-filter">
|
||||
<td>linky filter</td>
|
||||
<td>
|
||||
<pre><div ng-bind-html="snippet | linky"><br></div></pre>
|
||||
</td>
|
||||
<td>
|
||||
<div ng-bind-html="snippet | linky"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="escaped-html">
|
||||
<td>no filter</td>
|
||||
<td><pre><div ng-bind="snippet"><br></div></pre></td>
|
||||
<td><div ng-bind="snippet"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should linkify the snippet with urls', function() {
|
||||
expect(using('#linky-filter').binding('snippet | linky')).
|
||||
toBe('Pretty text with some links: ' +
|
||||
'<a href="http://angularjs.org/">http://angularjs.org/</a>, ' +
|
||||
'<a href="mailto:us@somewhere.org">us@somewhere.org</a>, ' +
|
||||
'<a href="mailto:another@somewhere.org">another@somewhere.org</a>, ' +
|
||||
'and one more: <a href="ftp://127.0.0.1/">ftp://127.0.0.1/</a>.');
|
||||
});
|
||||
|
||||
it ('should not linkify snippet without the linky filter', function() {
|
||||
expect(using('#escaped-html').binding('snippet')).
|
||||
toBe("Pretty text with some links:\n" +
|
||||
"http://angularjs.org/,\n" +
|
||||
"mailto:us@somewhere.org,\n" +
|
||||
"another@somewhere.org,\n" +
|
||||
"and one more: ftp://127.0.0.1/.");
|
||||
});
|
||||
|
||||
it('should update', function() {
|
||||
input('snippet').enter('new http://link.');
|
||||
expect(using('#linky-filter').binding('snippet | linky')).
|
||||
toBe('new <a href="http://link">http://link</a>.');
|
||||
expect(using('#escaped-html').binding('snippet')).toBe('new http://link.');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angular.module('ngSanitize').filter('linky', function() {
|
||||
var LINKY_URL_REGEXP = /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/,
|
||||
MAILTO_REGEXP = /^mailto:/;
|
||||
|
||||
return function(text) {
|
||||
if (!text) return text;
|
||||
var match;
|
||||
var raw = text;
|
||||
var html = [];
|
||||
// TODO(vojta): use $sanitize instead
|
||||
var writer = htmlSanitizeWriter(html);
|
||||
var url;
|
||||
var i;
|
||||
while ((match = raw.match(LINKY_URL_REGEXP))) {
|
||||
// We can not end in these as they are sometimes found at the end of the sentence
|
||||
url = match[0];
|
||||
// if we did not match ftp/http/mailto then assume mailto
|
||||
if (match[2] == match[3]) url = 'mailto:' + url;
|
||||
i = match.index;
|
||||
writer.chars(raw.substr(0, i));
|
||||
writer.start('a', {href:url});
|
||||
writer.chars(match[0].replace(MAILTO_REGEXP, ''));
|
||||
writer.end('a');
|
||||
raw = raw.substring(i + match[0].length);
|
||||
}
|
||||
writer.chars(raw);
|
||||
return html.join('');
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
})(window, window.angular);
|
|
@ -1,6 +0,0 @@
|
|||
/**
|
||||
* Configuration for jstd scenario adapter
|
||||
*/
|
||||
var jstdScenarioAdapter = {
|
||||
relativeUrlPrefix: '/build/docs/'
|
||||
};
|
|
@ -1,185 +0,0 @@
|
|||
/**
|
||||
* @license AngularJS v1.0.5
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* JSTestDriver adapter for angular scenario tests
|
||||
*
|
||||
* Example of jsTestDriver.conf for running scenario tests with JSTD:
|
||||
<pre>
|
||||
server: http://localhost:9877
|
||||
|
||||
load:
|
||||
- lib/angular-scenario.js
|
||||
- lib/jstd-scenario-adapter-config.js
|
||||
- lib/jstd-scenario-adapter.js
|
||||
# your test files go here #
|
||||
|
||||
proxy:
|
||||
- {matcher: "/your-prefix/*", server: "http://localhost:8000/"}
|
||||
</pre>
|
||||
*
|
||||
* For more information on how to configure jstd proxy, see {@link http://code.google.com/p/js-test-driver/wiki/Proxy}
|
||||
* Note the order of files - it's important !
|
||||
*
|
||||
* Example of jstd-scenario-adapter-config.js
|
||||
<pre>
|
||||
var jstdScenarioAdapter = {
|
||||
relativeUrlPrefix: '/your-prefix/'
|
||||
};
|
||||
</pre>
|
||||
*
|
||||
* Whenever you use <code>browser().navigateTo('relativeUrl')</code> in your scenario test, the relativeUrlPrefix will be prepended.
|
||||
* You have to configure this to work together with JSTD proxy.
|
||||
*
|
||||
* Let's assume you are using the above configuration (jsTestDriver.conf and jstd-scenario-adapter-config.js):
|
||||
* Now, when you call <code>browser().navigateTo('index.html')</code> in your scenario test, the browser will open /your-prefix/index.html.
|
||||
* That matches the proxy, so JSTD will proxy this request to http://localhost:8000/index.html.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Custom type of test case
|
||||
*
|
||||
* @const
|
||||
* @see jstestdriver.TestCaseInfo
|
||||
*/
|
||||
var SCENARIO_TYPE = 'scenario';
|
||||
|
||||
/**
|
||||
* Plugin for JSTestDriver
|
||||
* Connection point between scenario's jstd output and jstestdriver.
|
||||
*
|
||||
* @see jstestdriver.PluginRegistrar
|
||||
*/
|
||||
function JstdPlugin() {
|
||||
var nop = function() {};
|
||||
|
||||
this.reportResult = nop;
|
||||
this.reportEnd = nop;
|
||||
this.runScenario = nop;
|
||||
|
||||
this.name = 'Angular Scenario Adapter';
|
||||
|
||||
/**
|
||||
* Called for each JSTD TestCase
|
||||
*
|
||||
* Handles only SCENARIO_TYPE test cases. There should be only one fake TestCase.
|
||||
* Runs all scenario tests (under one fake TestCase) and report all results to JSTD.
|
||||
*
|
||||
* @param {jstestdriver.TestRunConfiguration} configuration
|
||||
* @param {Function} onTestDone
|
||||
* @param {Function} onAllTestsComplete
|
||||
* @returns {boolean} True if this type of test is handled by this plugin, false otherwise
|
||||
*/
|
||||
this.runTestConfiguration = function(configuration, onTestDone, onAllTestsComplete) {
|
||||
if (configuration.getTestCaseInfo().getType() != SCENARIO_TYPE) return false;
|
||||
|
||||
this.reportResult = onTestDone;
|
||||
this.reportEnd = onAllTestsComplete;
|
||||
this.runScenario();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
this.getTestRunsConfigurationFor = function(testCaseInfos, expressions, testRunsConfiguration) {
|
||||
testRunsConfiguration.push(
|
||||
new jstestdriver.TestRunConfiguration(
|
||||
new jstestdriver.TestCaseInfo(
|
||||
'Angular Scenario Tests', function() {}, SCENARIO_TYPE), []));
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Singleton instance of the plugin
|
||||
* Accessed using closure by:
|
||||
* - jstd output (reports to this plugin)
|
||||
* - initScenarioAdapter (register the plugin to jstd)
|
||||
*/
|
||||
var plugin = new JstdPlugin();
|
||||
|
||||
/**
|
||||
* Initialise scenario jstd-adapter
|
||||
* (only if jstestdriver is defined)
|
||||
*
|
||||
* @param {Object} jstestdriver Undefined when run from browser (without jstd)
|
||||
* @param {Function} initScenarioAndRun Function that inits scenario and runs all the tests
|
||||
* @param {Object=} config Configuration object, supported properties:
|
||||
* - relativeUrlPrefix: prefix for all relative links when navigateTo()
|
||||
*/
|
||||
function initScenarioAdapter(jstestdriver, initScenarioAndRun, config) {
|
||||
if (jstestdriver) {
|
||||
// create and register ScenarioPlugin
|
||||
jstestdriver.pluginRegistrar.register(plugin);
|
||||
plugin.runScenario = initScenarioAndRun;
|
||||
|
||||
/**
|
||||
* HACK (angular.scenario.Application.navigateTo)
|
||||
*
|
||||
* We need to navigate to relative urls when running from browser (without JSTD),
|
||||
* because we want to allow running scenario tests without creating its own virtual host.
|
||||
* For example: http://angular.local/build/docs/docs-scenario.html
|
||||
*
|
||||
* On the other hand, when running with JSTD, we need to navigate to absolute urls,
|
||||
* because of JSTD proxy. (proxy, because of same domain policy)
|
||||
*
|
||||
* So this hack is applied only if running with JSTD and change all relative urls to absolute.
|
||||
*/
|
||||
var appProto = angular.scenario.Application.prototype,
|
||||
navigateTo = appProto.navigateTo,
|
||||
relativeUrlPrefix = config && config.relativeUrlPrefix || '/';
|
||||
|
||||
appProto.navigateTo = function(url, loadFn, errorFn) {
|
||||
if (url.charAt(0) != '/' && url.charAt(0) != '#' &&
|
||||
url != 'about:blank' && !url.match(/^https?/)) {
|
||||
url = relativeUrlPrefix + url;
|
||||
}
|
||||
|
||||
return navigateTo.call(this, url, loadFn, errorFn);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds proper TestResult object from given model spec
|
||||
*
|
||||
* TODO(vojta) report error details
|
||||
*
|
||||
* @param {angular.scenario.ObjectModel.Spec} spec
|
||||
* @returns {jstestdriver.TestResult}
|
||||
*/
|
||||
function createTestResultFromSpec(spec) {
|
||||
var map = {
|
||||
success: 'PASSED',
|
||||
error: 'ERROR',
|
||||
failure: 'FAILED'
|
||||
};
|
||||
|
||||
return new jstestdriver.TestResult(
|
||||
spec.fullDefinitionName,
|
||||
spec.name,
|
||||
jstestdriver.TestResult.RESULT[map[spec.status]],
|
||||
spec.error || '',
|
||||
spec.line || '',
|
||||
spec.duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates JSTD output (jstestdriver.TestResult)
|
||||
*/
|
||||
angular.scenario.output('jstd', function(context, runner, model) {
|
||||
model.on('SpecEnd', function(spec) {
|
||||
plugin.reportResult(createTestResultFromSpec(spec));
|
||||
});
|
||||
|
||||
model.on('RunnerEnd', function() {
|
||||
plugin.reportEnd();
|
||||
});
|
||||
});
|
||||
initScenarioAdapter(window.jstestdriver, angular.scenario.setUpAndRun, window.jstdScenarioAdapter);
|
||||
})(window);
|
|
@ -1 +0,0 @@
|
|||
{"full":"1.0.7","major":"1","minor":"0","dot":"7","codename":"monochromatic-rainbow","cdn":"1.0.6"}
|
|
@ -1 +0,0 @@
|
|||
1.0.7
|
Before Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 12 KiB |
|
@ -1,109 +0,0 @@
|
|||
<div id="wrapper" class="container">
|
||||
<div class="row">
|
||||
<aside class="span3" data-ng-include data-src="'partials/application-menu.html'"></aside>
|
||||
<div id="actions-bg"></div>
|
||||
|
||||
<div id="container-right" class="span9">
|
||||
<h1 data-ng-show="create"><span class="gray">New Application</span></h1>
|
||||
|
||||
<h1 data-ng-hide="create">
|
||||
<span class="gray">{{application.name}}</span> configuration
|
||||
</h1>
|
||||
|
||||
<div data-ng-show="applicationForm.showErrors && applicationForm.$error.required" class="alert alert-error">
|
||||
Please fill in all required fields
|
||||
</div>
|
||||
<p class="subtitle subtitle-right"><span class="required">*</span> Required fields</p>
|
||||
|
||||
<form class="form-horizontal" name="applicationForm" novalidate>
|
||||
<fieldset>
|
||||
<legend>Settings</legend>
|
||||
|
||||
<div data-kc-input>
|
||||
<label>Name</label>
|
||||
<input class="input-xlarge" type="text" name="name" data-ng-model="application.name" autofocus
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div data-kc-input>
|
||||
<label>Enabled</label>
|
||||
<input class="input-xlarge" type="checkbox" name="enabled" data-ng-model="application.enabled">
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="realm">Realm <span class="required">*</span></label>
|
||||
|
||||
<div class="controls">
|
||||
<select data-ng-model="application.realm" id="realm" name="realm" data-ng-required>
|
||||
<option data-ng-repeat="r in realms" value="{{r.id}}"
|
||||
data-ng-selected="r.id == application.realm">{{r.name}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Roles</legend>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Roles</label>
|
||||
|
||||
<div class="controls">
|
||||
<span class="label" style="margin-right: 1em;"
|
||||
data-ng-repeat="r in (application.roles|orderBy:'toString()')">{{r}} <button
|
||||
data-ng-click="removeRole(r)"><i class="icon-remove icon-white"></i></button></span>
|
||||
|
||||
<div class="input-append">
|
||||
<input class="input-small" type="text" data-ng-model="newRole" placeHolder="Role"
|
||||
data-kc-enter="addRole()"/>
|
||||
<button class="btn" type="button" data-ng-click="addRole()">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Initial Roles</label>
|
||||
|
||||
<div class="controls">
|
||||
<span class="label" style="margin-right: 1em;"
|
||||
data-ng-repeat="r in (application.initialRoles|orderBy:'toString()')">{{r}} <button
|
||||
data-ng-click="removeInitialRole(r)"><i class="icon-remove icon-white"></i></button></span>
|
||||
|
||||
<div class="input-append">
|
||||
<select style="width: auto;" data-ng-model="newInitialRole"
|
||||
data-ng-click="addInitialRole()">
|
||||
<option data-ng-repeat="r in (application.roles|remove:application.initialRoles|orderBy:'toString()')"
|
||||
value="{{r}}">{{r}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-actions" data-ng-show="create">
|
||||
<button type="submit" data-ng-click="save()" class="btn btn-primary" data-ng-show="changed">Save
|
||||
</button>
|
||||
<button type="submit" data-ng-click="cancel()" class="btn" data-ng-click="cancel()"
|
||||
data-ng-show="changed">Cancel
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="form-actions" data-ng-show="!create">
|
||||
<button type="submit" data-ng-click="save()" class="btn btn-primary" data-ng-show="changed">Save
|
||||
changes
|
||||
</button>
|
||||
<button type="submit" data-ng-click="reset()" class="btn" data-ng-show="changed">Clear changes
|
||||
</button>
|
||||
<a href="#/applications" data-ng-hide="changed">View applications »</a>
|
||||
<button type="submit" data-ng-click="remove()" class="btn btn-danger" data-ng-hide="changed">
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="container-right-bg"></div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,26 +0,0 @@
|
|||
<div id="wrapper" class="container">
|
||||
<div class="row">
|
||||
<aside class="span3" data-ng-include data-src="'partials/application-menu.html'"></aside>
|
||||
<div id="actions-bg"></div>
|
||||
|
||||
<div id="container-right" class="span9">
|
||||
<a class="btn btn-small pull-right" href="#/create/application">Add Application</a>
|
||||
|
||||
<h1>
|
||||
<span class="gray">Applications</span>
|
||||
</h1>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Application</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr data-ng-repeat="application in applications">
|
||||
<td><a href="#/applications/{{application.id}}">{{application.name}}</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div id="container-right-bg"></div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,20 +0,0 @@
|
|||
<nav id="local-nav" data-ng-controller="ApplicationListCtrl">
|
||||
<ul class="nav nav-list">
|
||||
<li>
|
||||
<div>
|
||||
<span class="toggle">Applications</span>
|
||||
</div>
|
||||
<ul>
|
||||
<li data-ng-repeat="a in applications" data-ng-class="path[1] == a.id && 'active'">
|
||||
<a href="#/applications/{{a.id}}">{{a.name}}</a>
|
||||
<ul class="sub-items" data-ng-show="path[1] == a.id">
|
||||
<li data-ng-class="!path[2] && 'active'"><a href="#/applications/{{a.id}}">Configuration</a>
|
||||
</li>
|
||||
<li data-ng-class="path[2] == 'roles' && 'active'"><a href="#/applications/{{a.id}}/roles">Role
|
||||
mapping</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
|
@ -1,4 +0,0 @@
|
|||
<div id="wrapper" class="container">
|
||||
<div class="row">
|
||||
</div>
|
||||
</div>
|
|
@ -1,30 +0,0 @@
|
|||
<header class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<div class="nav-collapse">
|
||||
<nav id="global-nav">
|
||||
<ul class="nav">
|
||||
<li class="divider-vertical-left" data-ng-class="path[0] == '' && 'active'"><a href="#">Home</a>
|
||||
</li>
|
||||
<li class="divider-vertical-left" data-ng-class="path[0] == 'applications' && 'active'"
|
||||
data-ng-show="auth.loggedIn"><a href="#/applications">Applications</a></li>
|
||||
<li class="divider-vertical-left" data-ng-class="path[0] == 'realms' && 'active'"
|
||||
data-ng-show="auth.loggedIn"><a href="#/realms">Realms</a></li>
|
||||
</ul>
|
||||
<ul class="nav pull-right" data-ng-hide="auth.loggedIn">
|
||||
<li><a href="/ejs-identity/api/login/system">Login</a></li>
|
||||
<li><a href="/ejs-identity/api/register/system">Register</a></li>
|
||||
</ul>
|
||||
<ul class="nav pull-right" data-ng-show="auth.loggedIn">
|
||||
<li class="dropdown"><a data-toggle="dropdown" class="dropdown-toggle" href="#"><i
|
||||
class="icon-user icon-gray"></i> {{auth.user.displayName}} <i class="caret"></i></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="" data-ng-click="auth.logout()">Sign Out</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
|
@ -1,40 +0,0 @@
|
|||
<p>
|
||||
Open <a href="https://code.google.com/apis/console/" target="_blank">https://code.google.com/apis/console/</a>. From
|
||||
the
|
||||
drop-down menu select <i>Create</i>.
|
||||
</p>
|
||||
|
||||
<p>Use any name that you'd like, click <i>Create Project</i>, select <i>API Access</i> and click on <i>Create an OAuth
|
||||
2.0 client ID</i>.</p>
|
||||
|
||||
<p>Use any product name you'd like and leave the other fields empty, then click <i>Next</i>. On the next page select <i>Web
|
||||
application</i>
|
||||
as the application type. Click <i>more options</i> next to <i>Your site or hostname</i>. Fill in the form with the
|
||||
following values:</p>
|
||||
|
||||
<ul>
|
||||
<li><b>Authorized Redirect URIs:</b> {{callbackUrl}}</li>
|
||||
</ul>
|
||||
|
||||
<p>Click on <i>Create client ID</i>. Insert <i>Client ID</i> and <i>Client secret</i> in the form below.</p>
|
||||
|
||||
<form class="form-horizontal" name="googleHelpForm">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="providerHelp.key">Client ID </label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" class="input-xlarge" id="providerHelp.key"
|
||||
data-ng-model="application.providers[providerHelp.index].key">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="providerHelp.secret">Client secret </label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" class="input-xlarge" id="providerHelp.secret"
|
||||
data-ng-model="application.providers[providerHelp.index].secret">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p>Close the help panel and click <i>save changes</i>.</p>
|
|
@ -1,46 +0,0 @@
|
|||
<p>
|
||||
Open <a href="https://dev.twitter.com/apps" target="_blank">https://dev.twitter.com/apps</a>. Click on <i>Create a
|
||||
new
|
||||
application</i>.
|
||||
</p>
|
||||
|
||||
<p>Fill in name, description and website. For <i>Callback URL</i> use the following value:</p>
|
||||
|
||||
<ul>
|
||||
<li><b>Callback URL:</b> {{callbackUrl}}</li>
|
||||
</ul>
|
||||
|
||||
<p>Note: Twitter doesn't allow <b>localhost</b> as domain, use <b>127.0.0.1</b> instead!</p>
|
||||
|
||||
<p>Agree to the rules, fill in the captcha and click on <i>Create your Twitter application</i></p>
|
||||
|
||||
<p>Insert <i>Consumer key</i> and <i>Consumer secret</i> in the form below.</p>
|
||||
|
||||
<form class="form-horizontal" name="googleHelpForm">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="providerHelp.key">Consumer key </label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" class="input-xlarge" id="providerHelp.key"
|
||||
data-ng-model="application.providers[providerHelp.index].key">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="providerHelp.secret">Consumer secret </label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" class="input-xlarge" id="providerHelp.secret"
|
||||
data-ng-model="application.providers[providerHelp.index].secret">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p>
|
||||
Now click on <i>Settings</i> and tick the box <i>Allow this application to be used to Sign in with Twitter</i>, and
|
||||
click on <i>Update
|
||||
this Twitter application's settings</i>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Close the help panel and click <i>save changes</i>.
|
||||
</p>
|
|
@ -1,124 +0,0 @@
|
|||
<div id="wrapper" class="container">
|
||||
<div class="row">
|
||||
<aside class="span3" data-ng-include data-src="'partials/realm-menu.html'"></aside>
|
||||
<div id="actions-bg"></div>
|
||||
|
||||
<div id="container-right" class="span9">
|
||||
<h1 data-ng-show="create"><span class="gray">New Realm</span></h1>
|
||||
|
||||
<h1 data-ng-hide="create">
|
||||
<span class="gray">{{realm.name}}</span> configuration
|
||||
</h1>
|
||||
|
||||
<div data-ng-show="realmForm.showErrors && realmForm.$error.required" class="alert alert-error">Please fill
|
||||
in all required fields
|
||||
</div>
|
||||
<p class="subtitle subtitle-right"><span class="required">*</span> Required fields</p>
|
||||
|
||||
<form class="form-horizontal" name="realmForm" novalidate>
|
||||
<fieldset>
|
||||
<legend>Settings</legend>
|
||||
|
||||
<div data-kc-input>
|
||||
<label>Name</label>
|
||||
<input class="input-xlarge" type="text" name="name" data-ng-model="realm.name" autofocus
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div data-kc-input>
|
||||
<label>Enabled</label>
|
||||
<input class="input-xlarge" type="checkbox" name="enabled" data-ng-model="realm.enabled">
|
||||
</div>
|
||||
|
||||
<div data-kc-input>
|
||||
<label>Social login</label>
|
||||
<input class="input-xlarge" type="checkbox" name="social" data-ng-model="realm.social">
|
||||
</div>
|
||||
|
||||
<div data-kc-input>
|
||||
<label>User registration</label>
|
||||
<input class="input-xlarge" type="checkbox" name="social"
|
||||
data-ng-model="realm.userRegistration">
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="realmForm-tokenExpiration" class="control-label">Token expiration</label>
|
||||
|
||||
<div class="controls">
|
||||
<input class="input-small" type="text" name="tokenExpiration"
|
||||
data-ng-model="realm.tokenExpiration">
|
||||
<select style="width: auto;" name="tokenExpirationUnit"
|
||||
data-ng-model="realm.tokenExpirationUnit">
|
||||
<option value="SECONDS" data-ng-selected="!realm.tokenExpirationUnit">Seconds</option>
|
||||
<option value="MINUTES">Minutes</option>
|
||||
<option value="HOURS">Hours</option>
|
||||
<option value="DAYS">Days</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Roles</legend>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Roles</label>
|
||||
|
||||
<div class="controls">
|
||||
<span class="label" style="margin-right: 1em;"
|
||||
data-ng-repeat="r in (realm.roles|orderBy:'toString()')">{{r}} <button
|
||||
data-ng-click="removeRole(r)"><i class="icon-remove icon-white"></i></button></span>
|
||||
|
||||
<div class="input-append">
|
||||
<input class="input-small" type="text" data-ng-model="newRole" placeHolder="Role"
|
||||
data-kc-enter="addRole()"/>
|
||||
<button class="btn" type="button" data-ng-click="addRole()">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Initial Roles</label>
|
||||
|
||||
<div class="controls">
|
||||
<span class="label" style="margin-right: 1em;"
|
||||
data-ng-repeat="r in (realm.initialRoles|orderBy:'toString()')">{{r}} <button
|
||||
data-ng-click="removeInitialRole(r)"><i class="icon-remove icon-white"></i></button></span>
|
||||
|
||||
<div class="input-append">
|
||||
<select style="width: auto;" data-ng-model="newInitialRole"
|
||||
data-ng-click="addInitialRole()">
|
||||
<option data-ng-repeat="r in (realm.roles|remove:realm.initialRoles|orderBy:'toString()')"
|
||||
value="{{r}}">{{r}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-actions" data-ng-show="create">
|
||||
<button type="submit" data-ng-click="save()" class="btn btn-primary" data-ng-show="changed">Save
|
||||
</button>
|
||||
<button type="submit" data-ng-click="cancel()" class="btn" data-ng-click="cancel()"
|
||||
data-ng-show="changed">Cancel
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="form-actions" data-ng-show="!create">
|
||||
<button type="submit" data-ng-click="save()" class="btn btn-primary" data-ng-show="changed">Save
|
||||
changes
|
||||
</button>
|
||||
<button type="submit" data-ng-click="reset()" class="btn" data-ng-show="changed">Clear changes
|
||||
</button>
|
||||
<a href="#/realms" data-ng-hide="changed">View realms »</a>
|
||||
<button type="submit" data-ng-click="remove()" class="btn btn-danger" data-ng-hide="changed">
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div id="container-right-bg"></div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,27 +0,0 @@
|
|||
<div id="wrapper" class="container">
|
||||
<div class="row">
|
||||
<aside class="span3" data-ng-include data-src="'partials/realm-menu.html'"></aside>
|
||||
<div id="actions-bg"></div>
|
||||
|
||||
<div id="container-right" class="span9">
|
||||
<a class="btn btn-small pull-right" href="#/create/realm">Add Realm</a>
|
||||
|
||||
<h1>
|
||||
<span class="gray">Realms</span>
|
||||
</h1>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Realm</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr data-ng-repeat="r in realms">
|
||||
<td><a href="#/realms/{{r.id}}">{{r.name}}</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="container-right-bg"></div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,21 +0,0 @@
|
|||
<nav id="local-nav" data-ng-controller="RealmListCtrl">
|
||||
<ul class="nav nav-list">
|
||||
<li>
|
||||
<div>
|
||||
<span class="toggle">Realms</span>
|
||||
</div>
|
||||
<ul>
|
||||
<li data-ng-repeat="r in realms" data-ng-class="path[1] == r.id && 'active'">
|
||||
<a href=#/realms/{{r.id}}>{{r.name}}</a>
|
||||
<ul class="sub-items" data-ng-show="path[1] == r.id">
|
||||
<li data-ng-class="!path[2] && 'active'"><a href="#/realms/{{r.id}}">Configuration</a></li>
|
||||
<li data-ng-class="path[2] == 'users' && 'active'"><a href="#/realms/{{r.id}}/users">Users</a>
|
||||
</li>
|
||||
<li data-ng-class="path[2] == 'roles' && 'active'"><a href="#/realms/{{r.id}}/roles">Role
|
||||
mapping</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
|
@ -1,50 +0,0 @@
|
|||
<div id="wrapper" class="container">
|
||||
<div class="row">
|
||||
<aside class="span3" data-ng-include data-src="'partials/' + path[0].slice(0, -1) + '-menu.html'"></aside>
|
||||
|
||||
<div id="actions-bg"></div>
|
||||
|
||||
<div id="container-right" class="span9">
|
||||
<h1>
|
||||
<span class="gray" data-ng-hide="create">{{realm.name}}</span> role mapping
|
||||
</h1>
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<li data-ng-class="path[3] == r && 'active'" data-ng-repeat="r in (realm.roles|orderBy:'toString()')"><a
|
||||
href="#/{{path[0]}}/{{realm.id}}/roles/{{r}}">{{r}}</a></li>
|
||||
</ul>
|
||||
|
||||
<div data-ng-show="role">
|
||||
<select style="width: auto;" id="realm" name="realm" data-ng-model="newUser" data-ng-click="addUser(u)">
|
||||
<option data-ng-repeat="u in (allUsers|remove:users:'userId')" value="{{u.userId}}">{{u.userId}}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<th>Firstname</th>
|
||||
<th>Lastname</th>
|
||||
<th>Email</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr data-ng-repeat="user in users">
|
||||
<td>{{user.userId}}</td>
|
||||
<td>{{user.firstName}}</td>
|
||||
<td>{{user.lastName}}</td>
|
||||
<td>{{user.email}}</td>
|
||||
<td>
|
||||
<button data-ng-click="removeUser(user.userId)">
|
||||
<i class="icon-remove"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="container-right-bg"></div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,107 +0,0 @@
|
|||
<div id="wrapper" class="container">
|
||||
<div class="row">
|
||||
<aside class="span3" data-ng-include data-src="'partials/realm-menu.html'"></aside>
|
||||
<div id="actions-bg"></div>
|
||||
|
||||
<div id="container-right" class="span9">
|
||||
<h1 data-ng-show="create"><span class="gray">New User</span></h1>
|
||||
|
||||
<h1 data-ng-hide="create">
|
||||
<span class="gray">{{user.userId}}</span> configuration
|
||||
</h1>
|
||||
|
||||
<div data-ng-show="userForm.showErrors && userForm.$error.required" class="alert alert-error">Please fill in
|
||||
all required fields
|
||||
</div>
|
||||
<p class="subtitle subtitle-right"><span class="required">*</span> Required fields</p>
|
||||
|
||||
<form class="form-horizontal" name="userForm" novalidate>
|
||||
<fieldset>
|
||||
<legend>Details</legend>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="name">Username <span class="required">*</span></label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" class="input-xlarge" id="name" name="name" data-ng-model="user.userId"
|
||||
autofocus required data-ng-readonly="!create">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="email">Email </label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="email" class="input-xlarge" id="email" name="email" data-ng-model="user.email">
|
||||
<span class="help-inline error"
|
||||
data-ng-show="userForm.showErrors && userForm.email.$invalid">Invalid email</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="firstName">Firstname </label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" class="input-xlarge" id="firstName" data-ng-model="user.firstName">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="lastName">Lastname </label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" class="input-xlarge" id="lastName" data-ng-model="user.lastName">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="password">Password <span class="required">*</span></label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="password" class="input-xlarge" id="password" name="password"
|
||||
data-ng-model="user.password" data-ng-required="create">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset data-ng-show="user.attributes.length > 0">
|
||||
<legend>Attributes</legend>
|
||||
|
||||
<table class="table table-striped table-bordered margin-top">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr data-ng-repeat="attribute in user.attributes">
|
||||
<td><input type="text" placeholder="Name" value="{{attribute.name}}" readonly></td>
|
||||
<td><input type="text" placeholder="Value" value="{{attribute.value}}" readonly></td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-actions" data-ng-show="create">
|
||||
<button type="submit" data-ng-click="save()" class="btn btn-primary" data-ng-show="changed">Save
|
||||
</button>
|
||||
<button type="submit" data-ng-click="cancel()" class="btn" data-ng-click="cancel()"
|
||||
data-ng-show="changed">Cancel
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="form-actions" data-ng-show="!create">
|
||||
<button type="submit" data-ng-click="save()" class="btn btn-primary" data-ng-show="changed">Save
|
||||
changes
|
||||
</button>
|
||||
<button type="submit" data-ng-click="reset()" class="btn" data-ng-show="changed">Clear changes
|
||||
</button>
|
||||
<a href="#/realms/{{realm.id}}/users" data-ng-hide="changed">View users »</a>
|
||||
<button type="submit" data-ng-click="remove()" class="btn btn-danger" data-ng-hide="changed">
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div id="container-right-bg"></div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,32 +0,0 @@
|
|||
<div id="wrapper" class="container">
|
||||
<div class="row">
|
||||
<aside class="span3" data-ng-include data-src="'partials/realm-menu.html'"></aside>
|
||||
<div id="actions-bg"></div>
|
||||
|
||||
<div id="container-right" class="span9">
|
||||
<a class="btn btn-small pull-right" href="#/create/user/{{realm.id}}">Add User</a>
|
||||
|
||||
<h1>
|
||||
<span class="gray">{{realm.name}}</span> users
|
||||
</h1>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<th>Firstname</th>
|
||||
<th>Lastname</th>
|
||||
<th>Email</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr data-ng-repeat="user in users">
|
||||
<td><a href="#/realms/{{realm.id}}/users/{{user.userId}}">{{user.userId}}</a></td>
|
||||
<td>{{user.firstName}}</td>
|
||||
<td>{{user.lastName}}</td>
|
||||
<td>{{user.email}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div id="container-right-bg"></div>
|
||||
</div>
|
||||
</div>
|