<modelVersion>4.0.0</modelVersion>
<groupId>net.pterodactylus</groupId>
<artifactId>sone</artifactId>
- <version>0.7.5</version>
+ <version>0.7.6</version>
<dependencies>
<dependency>
<groupId>net.pterodactylus</groupId>
import net.pterodactylus.sone.data.Profile;
import net.pterodactylus.sone.data.Reply;
import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.data.Sone.ShowCustomAvatars;
import net.pterodactylus.sone.data.TemporaryImage;
import net.pterodactylus.sone.data.Profile.Field;
import net.pterodactylus.sone.fcp.FcpInterface;
sone.getOptions().addBooleanOption("ShowNotification/NewSones", new DefaultOption<Boolean>(true));
sone.getOptions().addBooleanOption("ShowNotification/NewPosts", new DefaultOption<Boolean>(true));
sone.getOptions().addBooleanOption("ShowNotification/NewReplies", new DefaultOption<Boolean>(true));
+ sone.getOptions().addEnumOption("ShowCustomAvatars", new DefaultOption<ShowCustomAvatars>(ShowCustomAvatars.NEVER));
+
followSone(sone, getSone("nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI"));
touchConfiguration();
return sone;
sone.getOptions().addBooleanOption("ShowNotification/NewSones", new DefaultOption<Boolean>(true));
sone.getOptions().addBooleanOption("ShowNotification/NewPosts", new DefaultOption<Boolean>(true));
sone.getOptions().addBooleanOption("ShowNotification/NewReplies", new DefaultOption<Boolean>(true));
+ sone.getOptions().addEnumOption("ShowCustomAvatars", new DefaultOption<ShowCustomAvatars>(ShowCustomAvatars.NEVER));
/* load Sone. */
String sonePrefix = "Sone/" + sone.getId();
String lastInsertFingerprint = configuration.getStringValue(sonePrefix + "/LastInsertFingerprint").getValue("");
/* load profile. */
- Profile profile = new Profile();
+ Profile profile = new Profile(sone);
profile.setFirstName(configuration.getStringValue(sonePrefix + "/Profile/FirstName").getValue(null));
profile.setMiddleName(configuration.getStringValue(sonePrefix + "/Profile/MiddleName").getValue(null));
profile.setLastName(configuration.getStringValue(sonePrefix + "/Profile/LastName").getValue(null));
album.addImage(image);
}
+ /* load avatar. */
+ String avatarId = configuration.getStringValue(sonePrefix + "/Profile/Avatar").getValue(null);
+ if (avatarId != null) {
+ profile.setAvatar(getImage(avatarId, false));
+ }
+
/* load options. */
sone.getOptions().getBooleanOption("AutoFollow").set(configuration.getBooleanValue(sonePrefix + "/Options/AutoFollow").getValue(null));
sone.getOptions().getBooleanOption("EnableSoneInsertNotifications").set(configuration.getBooleanValue(sonePrefix + "/Options/EnableSoneInsertNotifications").getValue(null));
sone.getOptions().getBooleanOption("ShowNotification/NewSones").set(configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewSones").getValue(null));
sone.getOptions().getBooleanOption("ShowNotification/NewPosts").set(configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewPosts").getValue(null));
sone.getOptions().getBooleanOption("ShowNotification/NewReplies").set(configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewReplies").getValue(null));
+ sone.getOptions().<ShowCustomAvatars> getEnumOption("ShowCustomAvatars").set(ShowCustomAvatars.valueOf(configuration.getStringValue(sonePrefix + "/Options/ShowCustomAvatars").getValue(ShowCustomAvatars.NEVER.name())));
/* if we’re still here, Sone was loaded successfully. */
synchronized (sone) {
configuration.getIntValue(sonePrefix + "/Profile/BirthDay").setValue(profile.getBirthDay());
configuration.getIntValue(sonePrefix + "/Profile/BirthMonth").setValue(profile.getBirthMonth());
configuration.getIntValue(sonePrefix + "/Profile/BirthYear").setValue(profile.getBirthYear());
+ configuration.getStringValue(sonePrefix + "/Profile/Avatar").setValue(profile.getAvatar());
/* save profile fields. */
int fieldCounter = 0;
configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewPosts").setValue(sone.getOptions().getBooleanOption("ShowNotification/NewPosts").getReal());
configuration.getBooleanValue(sonePrefix + "/Options/ShowNotification/NewReplies").setValue(sone.getOptions().getBooleanOption("ShowNotification/NewReplies").getReal());
configuration.getBooleanValue(sonePrefix + "/Options/EnableSoneInsertNotifications").setValue(sone.getOptions().getBooleanOption("EnableSoneInsertNotifications").getReal());
+ configuration.getStringValue(sonePrefix + "/Options/ShowCustomAvatars").setValue(sone.getOptions().<ShowCustomAvatars> getEnumOption("ShowCustomAvatars").get().name());
configuration.save();
/** Holds all {@link String} {@link Option}s. */
private final Map<String, Option<String>> stringOptions = Collections.synchronizedMap(new HashMap<String, Option<String>>());
+ /** Holds all {@link Enum} {@link Option}s. */
+ private final Map<String, Option<? extends Enum<?>>> enumOptions = Collections.synchronizedMap(new HashMap<String, Option<? extends Enum<?>>>());
+
/**
* Adds a boolean option.
*
return stringOptions.get(name);
}
+ /**
+ * Adds an {@link Enum} {@link Option}.
+ *
+ * @param name
+ * The name of the option
+ * @param enumOption
+ * The option
+ * @return The given option
+ */
+ public <T extends Enum<T>> Option<T> addEnumOption(String name, Option<T> enumOption) {
+ enumOptions.put(name, enumOption);
+ return enumOption;
+ }
+
+ /**
+ * Returns a {@link Enum} {@link Option}. As the type can probably not be
+ * interred correctly you could help the compiler by calling this method
+ * like this:
+ * <p>
+ *
+ * <pre>
+ * options.<SomeEnum> getEnumOption("SomeEnumOption").get();
+ * </pre>
+ *
+ * @param name
+ * The name of the option
+ * @return The enum option, or {@code null} if there is no enum option with
+ * the given name
+ */
+ public <T extends Enum<T>> Option<T> getEnumOption(String name) {
+ return (Option<T>) enumOptions.get(name);
+ }
+
}
Integer profileBirthDay = Numbers.safeParseInteger(profileXml.getValue("birth-day", null));
Integer profileBirthMonth = Numbers.safeParseInteger(profileXml.getValue("birth-month", null));
Integer profileBirthYear = Numbers.safeParseInteger(profileXml.getValue("birth-year", null));
- Profile profile = new Profile().setFirstName(profileFirstName).setMiddleName(profileMiddleName).setLastName(profileLastName);
+ Profile profile = new Profile(sone).setFirstName(profileFirstName).setMiddleName(profileMiddleName).setLastName(profileLastName);
profile.setBirthDay(profileBirthDay).setBirthMonth(profileBirthMonth).setBirthYear(profileBirthYear);
+ /* avatar is processed after images are loaded. */
+ String avatarId = profileXml.getValue("avatar", null);
/* parse profile fields. */
SimpleXML profileFieldsXml = profileXml.getNode("fields");
}
}
+ /* process avatar. */
+ if (avatarId != null) {
+ profile.setAvatar(core.getImage(avatarId, false));
+ }
+
/* okay, apparently everything was parsed correctly. Now import. */
/* atomic setter operation on the Sone. */
synchronized (sone) {
*/
public class Profile implements Fingerprintable {
+ /** The Sone this profile belongs to. */
+ private final Sone sone;
+
/** The first name. */
private volatile String firstName;
/** The year of the birth date. */
private volatile Integer birthYear;
+ /** The ID of the avatar image. */
+ private volatile String avatar;
+
/** Additional fields in the profile. */
private final List<Field> fields = Collections.synchronizedList(new ArrayList<Field>());
/**
* Creates a new empty profile.
+ *
+ * @param sone
+ * The Sone this profile belongs to
*/
- public Profile() {
- /* do nothing. */
+ public Profile(Sone sone) {
+ this.sone = sone;
}
/**
* The profile to copy
*/
public Profile(Profile profile) {
- if (profile == null) {
- return;
- }
+ this.sone = profile.sone;
this.firstName = profile.firstName;
this.middleName = profile.middleName;
this.lastName = profile.lastName;
this.birthDay = profile.birthDay;
this.birthMonth = profile.birthMonth;
this.birthYear = profile.birthYear;
+ this.avatar = profile.avatar;
this.fields.addAll(profile.fields);
}
//
/**
+ * Returns the Sone this profile belongs to.
+ *
+ * @return The Sone this profile belongs to
+ */
+ public Sone getSone() {
+ return sone;
+ }
+
+ /**
* Returns the first name.
*
* @return The first name
}
/**
+ * Returns the ID of the currently selected avatar image.
+ *
+ * @return The ID of the currently selected avatar image, or {@code null} if
+ * no avatar is selected.
+ */
+ public String getAvatar() {
+ return avatar;
+ }
+
+ /**
+ * Sets the avatar image.
+ *
+ * @param avatar
+ * The new avatar image, or {@code null} to not select an avatar
+ * image.
+ * @return This Sone
+ */
+ public Profile setAvatar(Image avatar) {
+ if (avatar == null) {
+ this.avatar = null;
+ return this;
+ }
+ Validation.begin().isEqual("Image Owner", avatar.getSone(), sone).check();
+ this.avatar = avatar.getId();
+ return this;
+ }
+
+ /**
* Sets the year of the birth date.
*
* @param birthYear
if (birthYear != null) {
fingerprint.append("BirthYear(").append(birthYear).append(')');
}
+ if (avatar != null) {
+ fingerprint.append("Avatar(").append(avatar).append(')');
+ }
fingerprint.append("ContactInformation(");
for (Field field : fields) {
fingerprint.append(field.getName()).append('(').append(field.getValue()).append(')');
*/
public class Sone implements Fingerprintable, Comparable<Sone> {
+ /**
+ * The possible values for the “show custom avatars” option.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+ public static enum ShowCustomAvatars {
+
+ /** Never show custom avatars. */
+ NEVER,
+
+ /** Only show custom avatars of followed Sones. */
+ FOLLOWED,
+
+ /** Only show custom avatars of Sones you manually trust. */
+ MANUALLY_TRUSTED,
+
+ /** Only show custom avatars of automatically trusted Sones. */
+ TRUSTED,
+
+ /** Always show custom avatars. */
+ ALWAYS,
+
+ }
+
/** comparator that sorts Sones by their nice name. */
public static final Comparator<Sone> NICE_NAME_COMPARATOR = new Comparator<Sone>() {
private volatile long time;
/** The profile of this Sone. */
- private volatile Profile profile = new Profile();
+ private volatile Profile profile = new Profile(this);
/** The client used by the Sone. */
private volatile Client client;
}
/** The version. */
- public static final Version VERSION = new Version(0, 7, 5);
+ public static final Version VERSION = new Version(0, 7, 6);
/** The logger. */
private static final Logger logger = Logging.getLogger(SonePlugin.class);
import java.io.StringWriter;
import java.util.Map;
+import net.pterodactylus.sone.core.Core;
import net.pterodactylus.sone.data.Image;
import net.pterodactylus.util.number.Numbers;
import net.pterodactylus.util.object.Default;
/** The template to render for the <img> tag. */
private static final Template linkTemplate = TemplateParser.parse(new StringReader("<img<%ifnull !class> class=\"<%class|css>\"<%/if> src=\"<%src|html><%if forceDownload>?forcedownload=true<%/if>\" alt=\"<%alt|html>\" title=\"<%title|html>\" width=\"<%width|html>\" height=\"<%height|html>\" style=\"position: relative;<%ifnull ! top>top: <% top|html>;<%/if><%ifnull ! left>left: <% left|html>;<%/if>\"/>"));
+ /** The core. */
+ private final Core core;
+
/** The template context factory. */
private final TemplateContextFactory templateContextFactory;
/**
* Creates a new image link filter.
*
+ * @param core
+ * The core
* @param templateContextFactory
* The template context factory
*/
- public ImageLinkFilter(TemplateContextFactory templateContextFactory) {
+ public ImageLinkFilter(Core core, TemplateContextFactory templateContextFactory) {
+ this.core = core;
this.templateContextFactory = templateContextFactory;
}
*/
@Override
public Object format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
- Image image = (Image) data;
+ Image image = null;
+ if (data instanceof String) {
+ image = core.getImage((String) data, false);
+ } else if (data instanceof Image) {
+ image = (Image) data;
+ } else {
+ return null;
+ }
String imageClass = parameters.get("class");
int maxWidth = Numbers.safeParseInteger(parameters.get("max-width"), Integer.MAX_VALUE);
int maxHeight = Numbers.safeParseInteger(parameters.get("max-height"), Integer.MAX_VALUE);
--- /dev/null
+/*
+ * Sone - ProfileAccessor.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.template;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.data.Profile;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.data.Sone.ShowCustomAvatars;
+import net.pterodactylus.sone.freenet.wot.Trust;
+import net.pterodactylus.util.template.Accessor;
+import net.pterodactylus.util.template.ReflectionAccessor;
+import net.pterodactylus.util.template.TemplateContext;
+
+/**
+ * {@link Accessor} for {@link Profile} objects that overwrites the original
+ * “avatar” member to include checks for whether the custom avatar should
+ * actually be shown.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class ProfileAccessor extends ReflectionAccessor {
+
+ /** The core. */
+ private final Core core;
+
+ /**
+ * Creates a new profile accessor.
+ *
+ * @param core
+ * The Sone core
+ */
+ public ProfileAccessor(Core core) {
+ this.core = core;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object get(TemplateContext templateContext, Object object, String member) {
+ Profile profile = (Profile) object;
+ if ("avatar".equals(member)) {
+ Sone currentSone = (Sone) templateContext.get("currentSone");
+ if (currentSone == null) {
+ /* not logged in? don’t show custom avatars, then. */
+ return null;
+ }
+ Sone remoteSone = profile.getSone();
+ if (core.isLocalSone(remoteSone)) {
+ /* always show your own avatars. */
+ return profile.getAvatar();
+ }
+ ShowCustomAvatars showCustomAvatars = currentSone.getOptions().<ShowCustomAvatars> getEnumOption("ShowCustomAvatars").get();
+ if (showCustomAvatars == ShowCustomAvatars.NEVER) {
+ return null;
+ }
+ String avatarId = profile.getAvatar();
+ if ((showCustomAvatars == ShowCustomAvatars.ALWAYS) || (avatarId == null)) {
+ return avatarId;
+ }
+ if ((showCustomAvatars == ShowCustomAvatars.FOLLOWED) && currentSone.hasFriend(remoteSone.getId())) {
+ return avatarId;
+ }
+ Trust trust = core.getTrust(currentSone, remoteSone);
+ if (trust == null) {
+ return null;
+ }
+ if ((showCustomAvatars == ShowCustomAvatars.MANUALLY_TRUSTED) && (trust.getExplicit() != null) && (trust.getExplicit() > 0)) {
+ return avatarId;
+ }
+ if ((showCustomAvatars == ShowCustomAvatars.TRUSTED) && ((trust.getExplicit() != null) && (trust.getExplicit() > 0)) || ((trust.getImplicit() != null) && (trust.getImplicit() > 0))) {
+ return avatarId;
+ }
+ return null;
+ }
+ return super.get(templateContext, object, member);
+ }
+
+}
Integer birthDay = profile.getBirthDay();
Integer birthMonth = profile.getBirthMonth();
Integer birthYear = profile.getBirthYear();
+ String avatarId = profile.getAvatar();
List<Field> fields = profile.getFields();
if (request.getMethod() == Method.POST) {
if (request.getHttpRequest().getPartAsStringFailsafe("save-profile", 4).equals("true")) {
birthDay = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("birth-day", 256).trim());
birthMonth = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("birth-month", 256).trim());
birthYear = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("birth-year", 256).trim());
+ avatarId = request.getHttpRequest().getPartAsStringFailsafe("avatar-id", 36);
profile.setFirstName(firstName.length() > 0 ? firstName : null);
profile.setMiddleName(middleName.length() > 0 ? middleName : null);
profile.setLastName(lastName.length() > 0 ? lastName : null);
profile.setBirthDay(birthDay).setBirthMonth(birthMonth).setBirthYear(birthYear);
+ profile.setAvatar(webInterface.getCore().getImage(avatarId, false));
for (Field field : fields) {
String value = request.getHttpRequest().getPartAsStringFailsafe("field-" + field.getId(), 400);
field.setValue(value);
templateContext.set("birthDay", birthDay);
templateContext.set("birthMonth", birthMonth);
templateContext.set("birthYear", birthYear);
+ templateContext.set("avatar-id", avatarId);
templateContext.set("fields", fields);
}
import net.pterodactylus.sone.core.Core.Preferences;
import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.data.Sone.ShowCustomAvatars;
import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired;
import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.number.Numbers;
currentSone.getOptions().getBooleanOption("ShowNotification/NewPosts").set(showNotificationNewPosts);
boolean showNotificationNewReplies = request.getHttpRequest().isPartSet("show-notification-new-replies");
currentSone.getOptions().getBooleanOption("ShowNotification/NewReplies").set(showNotificationNewReplies);
+ String showCustomAvatars = request.getHttpRequest().getPartAsStringFailsafe("show-custom-avatars", 32);
+ currentSone.getOptions().<ShowCustomAvatars> getEnumOption("ShowCustomAvatars").set(ShowCustomAvatars.valueOf(showCustomAvatars));
webInterface.getCore().touchConfiguration();
}
Integer insertionDelay = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("insertion-delay", 16));
templateContext.set("show-notification-new-sones", currentSone.getOptions().getBooleanOption("ShowNotification/NewSones").get());
templateContext.set("show-notification-new-posts", currentSone.getOptions().getBooleanOption("ShowNotification/NewPosts").get());
templateContext.set("show-notification-new-replies", currentSone.getOptions().getBooleanOption("ShowNotification/NewReplies").get());
+ templateContext.set("show-custom-avatars", currentSone.getOptions().<ShowCustomAvatars> getEnumOption("ShowCustomAvatars").get().name());
}
templateContext.set("insertion-delay", preferences.getInsertionDelay());
templateContext.set("posts-per-page", preferences.getPostsPerPage());
import net.pterodactylus.sone.data.Image;
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.PostReply;
+import net.pterodactylus.sone.data.Profile;
import net.pterodactylus.sone.data.Reply;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.freenet.L10nFilter;
import net.pterodactylus.sone.template.JavascriptFilter;
import net.pterodactylus.sone.template.ParserFilter;
import net.pterodactylus.sone.template.PostAccessor;
+import net.pterodactylus.sone.template.ProfileAccessor;
import net.pterodactylus.sone.template.ReplyAccessor;
import net.pterodactylus.sone.template.ReplyGroupFilter;
import net.pterodactylus.sone.template.RequestChangeFilter;
templateContextFactory.addAccessor(Identity.class, new IdentityAccessor(getCore()));
templateContextFactory.addAccessor(Trust.class, new TrustAccessor());
templateContextFactory.addAccessor(HTTPRequest.class, new HttpRequestAccessor());
+ templateContextFactory.addAccessor(Profile.class, new ProfileAccessor(getCore()));
templateContextFactory.addFilter("date", new DateFilter());
templateContextFactory.addFilter("html", new HtmlFilter());
templateContextFactory.addFilter("replace", new ReplaceFilter());
templateContextFactory.addFilter("unknown", new UnknownDateFilter(getL10n(), "View.Sone.Text.UnknownDate"));
templateContextFactory.addFilter("format", new FormatFilter());
templateContextFactory.addFilter("sort", new CollectionSortFilter());
- templateContextFactory.addFilter("image-link", new ImageLinkFilter(templateContextFactory));
+ templateContextFactory.addFilter("image-link", new ImageLinkFilter(getCore(), templateContextFactory));
templateContextFactory.addFilter("replyGroup", new ReplyGroupFilter());
templateContextFactory.addFilter("in", new ContainsFilter());
templateContextFactory.addFilter("unique", new UniqueElementFilter());
Page.Options.Option.ShowNotificationNewSones.Description=Show notifications for new Sones.
Page.Options.Option.ShowNotificationNewPosts.Description=Show notifications for new posts.
Page.Options.Option.ShowNotificationNewReplies.Description=Show notifications for new replies.
+Page.Options.Section.AvatarOptions.Title=Avatar Options
+Page.Options.Option.ShowAvatars.Description=You can disable custom avatars here, depending on the selected criteria. If an avatar is disabled for a Sone, the automatically generated avatar is shown instead.
+Page.Options.Option.ShowAvatars.Never.Description=Never show custom avatars.
+Page.Options.Option.ShowAvatars.Followed.Description=Only show avatars for Sones that you follow.
+Page.Options.Option.ShowAvatars.ManuallyTrusted.Description=Only show avatars for Sones that you have manually assigned a trust value larger than 0 to.
+Page.Options.Option.ShowAvatars.Trusted.Description=Only show avatars for Sones that have a trust value larger than 0.
+Page.Options.Option.ShowAvatars.Always.Description=Always show custom avatars. Be warned: some avatars might contain disturbing or offensive imagery.
Page.Options.Section.RuntimeOptions.Title=Runtime Behaviour
Page.Options.Option.InsertionDelay.Description=The number of seconds the Sone inserter waits after a modification of a Sone before it is being inserted.
Page.Options.Option.PostsPerPage.Description=The number of posts to display on a page before pagination controls are being shown.
Page.EditProfile.Birthday.Label.Day=Day:
Page.EditProfile.Birthday.Label.Month=Month:
Page.EditProfile.Birthday.Label.Year=Year:
+Page.EditProfile.Avatar.Title=Avatar
+Page.EditProfile.Avatar.Description=You can select one of your uploaded images to be shown as avatar. It should not be larger than 64×64 pixels because that is the largest size shown for other people (80×80 pixels is used for the page header).
+Page.EditProfile.Avatar.Delete=No avatar
Page.EditProfile.Fields.Title=Custom Fields
Page.EditProfile.Fields.Description=Here you can enter custom fields into your profile. These fields can contain anything you want and be as terse or as verbose as you wish. Just remember that when it comes to anonymity, sometimes less is more.
Page.EditProfile.Fields.Button.Edit=edit
margin-bottom: 1ex;
}
+#sone .profile-avatar {
+ display: inline-block;
+ width: 80px;
+ height: 80px;
+ overflow: hidden;
+ position: absolute;
+}
+
#sone .profile-link {
font-weight: bold;
color: rgb(28, 131, 191);
margin-right: 1ex;
}
+#sone .menu-avatar {
+ display: inline-block;
+ width: 64px;
+ height: 64px;
+ overflow: hidden;
+}
+
#sone .post .sone-menu .inner-menu {
margin-left: 64px;
padding-left: 1ex;
position: absolute;
}
+#sone .post-avatar {
+ display: inline-block;
+ width: 48px;
+ height: 48px;
+ overflow: hidden;
+}
+
#sone .post > .inner-part {
margin-left: 48px;
padding-left: 0.5em;
position: absolute;
}
+#sone .reply-avatar {
+ display: inline-block;
+ width: 36px;
+ height: 36px;
+ overflow: hidden;
+}
+
#sone .post .reply > .inner-part {
margin-left: 36px;
padding-left: 0.5em;
#sone #sort-options {
margin-bottom: 1em;
}
+
+#sone ul#avatar-selection {
+ padding: 0;
+}
+
+#sone #avatar-selection li {
+ display: inline-block;
+}
+
+#sone #avatar-selection li .post-avatar {
+ vertical-align: middle;
+ margin-top: 0.5em;
+}
+
+#sone #avatar-selection li#no-avatar {
+ display: block;
+}
+
+#sone form#options ul {
+ padding-left: 1em;
+}
+
+#sone form#options li {
+ list-style-type: none;
+}
<input type="text" name="birth-year" value="<% birthYear|html>" />
</div>
+ <h1><%= Page.EditProfile.Avatar.Title|l10n|html></h1>
+
+ <p><%= Page.EditProfile.Avatar.Description|l10n|html></p>
+
+ <ul id="avatar-selection">
+ <li id="no-avatar">
+ <input type="radio" name="avatar-id" value="none"<%ifnull avatar-image> checked="checked"<%/if>/>
+ <%= Page.EditProfile.Avatar.Delete|l10n|html>
+ </li>
+ <%foreach currentSone.allImages image>
+ <li>
+ <input type="radio" name="avatar-id" value="<%image.id|html>"<%if avatar-id|match key=image.id> checked="checked"<%/if>/>
+ <div class="post-avatar"><% image|image-link max-width=48 max-height=48 mode=enlarge title==image.title></div>
+ </li>
+ <%/foreach>
+ </ul>
+
<div>
<button type="submit" name="save-profile" value="true"><%= Page.EditProfile.Button.Save|l10n|html></button>
</div>
</div>
<div id="profile" class="<%ifnull currentSone>offline<%else>online<%/if>">
- <a class="picture" href="index.html">
- <%ifnull !currentSone>
- <img src="/WebOfTrust/GetIdenticon?identity=<% currentSone.id|html>&width=80&height=80" width="80" height="80" alt="Profile Avatar" />
- <%else>
- <img src="images/sone.png" width="80" height="80" alt="Sone is offline" />
- <%/if>
- </a>
+ <div class="avatar profile-avatar">
+ <a class="picture" href="index.html">
+ <%ifnull !currentSone>
+ <%ifnull !currentSone.profile.avatar>
+ <%currentSone.profile.avatar|image-link max-width=80 max-height=80 mode=enlarge title="Profile Avatar">
+ <%else>
+ <img src="/WebOfTrust/GetIdenticon?identity=<% currentSone.id|html>&width=80&height=80" width="80" height="80" alt="Profile Avatar" />
+ <%/if>
+ <%else>
+ <img src="images/sone.png" width="80" height="80" alt="Sone is offline" />
+ <%/if>
+ </a>
+ </div>
<%ifnull ! currentSone>
<div id="home-sone">
<% currentSone|store key=sone>
<div class="sone-menu <% class|css|html>">
<div class="sone-menu-id hidden"><%sone.id|html></div>
- <img class="avatar" src="/WebOfTrust/GetIdenticon?identity=<%sone.id|html>&width=64&height=64" width="64" height="64" alt="Avatar Image" />
+ <div class="avatar menu-avatar">
+ <%ifnull !sone.profile.avatar>
+ <%sone.profile.avatar|image-link max-width=64 max-height=64 mode=enlarge title==sone.niceName>
+ <%else>
+ <img src="/WebOfTrust/GetIdenticon?identity=<%sone.id|html>&width=64&height=64" width="64" height="64" alt="Avatar Image" />
+ <%/if>
+ </div>
<div class="inner-menu">
<div>
<a class="author" href="viewSone.html?sone=<%sone.id|html>"><%sone.niceName|html></a>
<%include include/soneMenu.html class=="sone-post-menu" sone=post.sone>
<div class="avatar post-avatar" >
<%if post.loaded>
- <img src="/WebOfTrust/GetIdenticon?identity=<% post.sone.id|html>&width=48&height=48" width="48" height="48" alt="Avatar Image" />
+ <%ifnull !post.sone.profile.avatar>
+ <%post.sone.profile.avatar|image-link max-width=48 max-height=48 mode=enlarge title="Avatar Image">
+ <%else>
+ <img src="/WebOfTrust/GetIdenticon?identity=<% post.sone.id|html>&width=48&height=48" width="48" height="48" alt="Avatar Image" />
+ <%/if>
<%else>
<img src="images/sone-avatar.png" width="48" height="48" alt="Avatar Image" />
<%/if>
<div class="reply-author-local hidden"><% reply.sone.local></div>
<%include include/soneMenu.html class=="sone-reply-menu" sone=reply.sone>
<div class="avatar reply-avatar">
- <img src="/WebOfTrust/GetIdenticon?identity=<% reply.sone.id|html>&width=36&height=36" width="36" height="36" alt="Avatar Image" />
+ <%ifnull !reply.sone.profile.avatar>
+ <% reply.sone.profile.avatar|image-link max-width=36 max-height=36 mode=enlarge title="Avatar Image">
+ <%else>
+ <img src="/WebOfTrust/GetIdenticon?identity=<% reply.sone.id|html>&width=36&height=36" width="36" height="36" alt="Avatar Image" />
+ <%/if>
</div>
<div class="inner-part">
<div>
<birth-day><% currentSone.profile.birthDay|xml></birth-day>
<birth-month><% currentSone.profile.birthMonth|xml></birth-month>
<birth-year><% currentSone.profile.birthYear|xml></birth-year>
+ <avatar><%currentSone.profile.avatar|xml></avatar>
<fields>
<%foreach currentSone.profile.fields field>
<field>
<%= Page.Options.Option.ShowNotificationNewReplies.Description|l10n|html>
</p>
+ <h2><%= Page.Options.Section.AvatarOptions.Title|l10n|html></h2>
+
+ <p><%= Page.Options.Option.ShowAvatars.Description|l10n|html></p>
+
+ <ul>
+ <li>
+ <input type="radio" name="show-custom-avatars" value="NEVER"<%if show-custom-avatars|match value=NEVER> checked="checked"<%/if>/>
+ <%=Page.Options.Option.ShowAvatars.Never.Description|l10n|html>
+ </li>
+ <li>
+ <input type="radio" name="show-custom-avatars" value="FOLLOWED"<%if show-custom-avatars|match value=FOLLOWED> checked="checked"<%/if>/>
+ <%=Page.Options.Option.ShowAvatars.Followed.Description|l10n|html>
+ </li>
+ <li>
+ <input type="radio" name="show-custom-avatars" value="MANUALLY_TRUSTED"<%if show-custom-avatars|match value=MANUALLY_TRUSTED> checked="checked"<%/if>/>
+ <%=Page.Options.Option.ShowAvatars.ManuallyTrusted.Description|l10n|html>
+ </li>
+ <li>
+ <input type="radio" name="show-custom-avatars" value="TRUSTED"<%if show-custom-avatars|match value=TRUSTED> checked="checked"<%/if>/>
+ <%=Page.Options.Option.ShowAvatars.Trusted.Description|l10n|html>
+ </li>
+ <li>
+ <input type="radio" name="show-custom-avatars" value="ALWAYS"<%if show-custom-avatars|match value=ALWAYS> checked="checked"<%/if>/>
+ <%=Page.Options.Option.ShowAvatars.Always.Description|l10n|html>
+ </li>
+ </ul>
+
<h2><%= Page.Options.Section.RuntimeOptions.Title|l10n|html></h2>
<p><%= Page.Options.Option.InsertionDelay.Description|l10n|html></p>