Jump to content

Rikki

Members
  • Joined

  • Last visited

Everything posted by Rikki

  1. Rikki posted a guide in Using UI widgets
    Initializing widgets UI widgets can now be created on elements without needing to write any code. This is done by specifying special attributes on the element using the HTML5 data attributes. Widgets each define their own behaviors - some wait for user interactions, others might immediately change the display when executed. Usage Each UI widget has a base attribute that identifies it, and causes the element to be passed to the widget module to be handled. All widget attributes have the prefix ips. <!-- If a widget called 'Widget' existed, we could initialize it on an element like this --> <div data-ipsWidget> ... </div> In this example, ipsWidget would be referred to as the widget key. The data- part of the HTML attribute is implied throughout the documentation for individual widgets. Each element can have multiple widgets specified; simply add each necessary widget prefix to the element as above. That said, bear in mind multiple widgets on a single element might cause conflicts if more than one responds to a user interaction. Test to be sure. Note: In many cases, the helpers available in the IPS4 PHP framework will generate all of the required HTML for you, including widget attributes, meaning you won't need to manually specify UI widgets in your HTML. For example, the autocomplete form input type adds the necessary attributes, so you don't need to handle it yourself. Consult the PHP documentation for more information. Passing options Most widgets have a number of options that control their behavior. To pass an option, use this format: <div data-ipsWidget data-ipsWidget-option1='Option value' data-ipsWidget-option2='Another option value' > ... </div> Each option is added as an additional attribute on the element, with the attribute key being data-ipsWidget- followed by the option name. By default, options are passed as strings because they come from the DOM. However, if the option contains only numbers, it will be cast explicitly as a number when passed into the widget. If the option contains the strings true or false, it will be cast as a boolean. In addition, options without a provided value are treated as the boolean true, meaning it is possible to specify true values like so: <div data-ipsWidget data-ipsWidget-showWide> Automatic initialization Widgets are automatically initialized when the data API is used as described. Further to that, when new content is loaded into the page (such as when an AJAX request fires), any widgets within the new content will also be automatically initialized.
  2. What's a widget? UI widgets are modules that: Are instantiated on individual DOM nodes Provide some kind of UI functionality Benefit from being reusable Come with an automatic data API functionality A UI widget is created as a standard module, under the ips.ui namespace. Basic widget boilerplate ;( function($, _, undefined){ "use strict"; ips.createModule('ips.ui.widgetName', function(){ var respond = function (elem, options, e) { }; // Register this module as a widget to enable the data API ips.ui.registerWidget( 'widgetName', ips.ui.widgetName ); return { respond: respond }; }); }(jQuery, _)); In the example above, a module is created with the module path ips.ui.widgetName. UI widget modules must expose a response method, either the default respond method or by manually specifying a method when the widget is registered (see below). This is the method that is called when the widget is instantiated. Registering a UI widget A module is registered as a widget by calling the ips.ui.registerWidget method before the module returns its public methods object. ips.ui.registerWidget( string widgetKey, object handlerModule [, array acceptedOptions] [, object widgetOptions] [, function callback] ); The method takes the following parameters: widgetKey (required) The key that is used to reference this widget throughout the software. The key is prefixed with ips when used, to prevent naming collisions. The key forms both the data API attribute key, and the name of the jQuery plugin that can be used to instantiate the widget. Assuming the widget key is set to widget, the data API attribute key and jQuery plugin would be: data-ipsWidget // Data API attribute $('element').ipsWidget(); // jQuery plugin method handlerModule (required) A reference to the module that will respond when the widget is instantiated. In practice, this should be a self reference to the module that is being defined. acceptedOptions (optional) An array of strings defining the options that this widget will accept. When the widget is instantiated, if these options are defined their values will be passed back to the respond method. widgetOptions (optional) An object containing options for how this widget will be registered or instantiated. The available options are: lazyLoad By default, widgets are instantiated as soon as they are seen in the DOM. By setting this option to true the widget is not instantiated until the user interacts with the element. lazyEvent If the lazyLoad option is enabled, this option defines the event that will be watched for. When the event name set here is triggered, the widget will be initialized. makejQueryPlugin By default, widgets will have an associated jQuery plugin created to allow them to be instantiated programatically. This functionality can be disabled by setting this option to false. callback (optional) A widget must have a response method, which is called when it is initialized on an element. By default, a method with the name respond is looked for, but a different response function can be set by passing it as this parameter. Events The javascript framework in IPS4 relies heavily on events. Controllers in particular are completely decoupled and cannot directly communicate with one another. Instead, communication happens by triggering and receiving events. It is therefore important that your widget emits events when key actions happen. This allows controllers, other UI widgets, even third-party scripts to respond when something happens within your widget. As an example, the ips.ui.menu widget emits a number of events, including menuOpened, menuClosed and menuItemSelected. Each of these events also provides a number of data items relevant to the event, which can be used by event handlers listening for those events. It is this event interaction that facilitates dynamic pages within the IPS Community Suite, so it's important your widget also emits relevant events. When you emit an event, it is usually most appropriate to emit it on the element the widget is registered on. Emitting an event uses the standard jQuery syntax: $( elem ).trigger( 'myWidgetEvent', { color: 'red', size: 'large' } ); Widget initialization A widget is initialized either as soon as it is seen in the DOM, or, if the lazyLoad option is enabled, when the user interacts with the element the widget is created on. In both cases, the initialization process is the same. The internal widget manager will call the response method for the widget (respond by default, or whatever function is passed as the callback option), passing in the following parameters: function respond( element elem, array options, event ev ) elem A reference to the element on which the widget has been initialized options An array of options that have been specified on the element, and which were in the list of expected options when the widget was first registered. Any other options are ignored and won't be passed through. ev If the widget is initialized in response to a user interaction because lazyLoad is enabled, this parameter will contain the original event object. It will be undefined if the widget is being initialized on load. Example widget Let's take a trivial example, and assume we're creating a widget which hides an element: ;( function($, _, undefined){ "use strict"; ips.createModule('ips.ui.hideElem', function(){ var respond = function (elem, options, e) { if( options.animate ){ $( elem ).fadeOut(); } else { $( elem ).hide(); } $( elem ).trigger( 'hiddenElement' ); }; ips.ui.registerWidget( 'hideElem', ips.ui.hideElem, [ 'animate' ], { lazyLoad: true, lazyEvent: 'click' } ); return { respond: respond }; }); }(jQuery, _)); When we register the widget, we set up an animate option in the acceptedOptions parameter. In the widgetOptions parameter, we set lazyLoad to true, and the lazyEvent to click - meaning our widget won't be initialized until the user clicks on the element. In our respond method, we simply check if options.animate is true, fading out if so and hiding if not. This example widget could be initialized on an element like so: <button data-ipsHideElem data-ipsHideElem-animate='true'>Click to hide me</button>
  3. Overview Controllers are special modules that handle specific functionality on specific pages. They are not necessarily reusable in different contexts, although some may be. A controller is initialized on an individual element, and that element becomes the controller's scope. A controller responds to user events (such as click) and events triggered by UI widgets or sub-controllers, and manipulates its scope element accordingly. The scope of a controllers functionality is entirely flexible. A controller might be initialized on a very small fragment of the page and perform just one task, or it might be initialized on a main wrapper and handle functionality that applies to the whole page. A controller can be initialized on an element even if it's a descendent of another element with a controller; this is a common pattern whereby the child controller can emit events that the parent controller can respond to. Generally, keep a controller narrowly focused on one aspect of the page. If a controller ends up handing distinct and unrelated functionality, split it into smaller controllers. Creating a controller A controller is registered in a slightly different way to standard modules; instead, an object literal is passed to the method ips.controller.register: ;( function($, _, undefined){ "use strict"; ips.controller.register('type.controllerID', { initialize: function () { // Controller events are initialized here } }); }(jQuery, _)); A controller, at the very least, must define an initialize method, which is called when the controller is initialized on an element. The initialize method should set up events that are handled by this controller only; if other initialization tasks are necessary, it is recommended that you define a setup method within the controller, and call that at the start or end of the initialize method as appropriate. Within the controller definition, this refers to the controller instance. That means controller methods can be called internally with this.someMethod(). A reference to the element that the controller is initialized on (its scope) is available with the scope property: // An example which hides the element this.scope.hide(); Method and property names For clarity, it is recommended that event handler method names (i.e. the handlers to events you set up in initialize) are not prefixed with an underscore, while other methods and properties of the controller are. This helps create a clear distinction between core functionality of the controller (the event handlers) and supporting methods. Handling events Event handling is the core purpose of a controller. There are three special methods available for watching and triggering events. Two are named after their jQuery counterparts, but add controller-specific behavior. this.on Used for watching and responding to events. this.on( [element elem,] string eventType [, selector delegate], function callback ); elem A reference to the element on which the event will be watched for. If not specified, this.scope is used by default. eventType The event being watched for. This can be a default browser event, like click, or events from widgets and models, like menuItemSelected. delegate A delegate selector can optionally be specified. See the jQuery documentation for more information. callback A callback function, called when this event is observed. The function specified here is automatically bound to this, so internal methods can be passed simply by referencing them like so: this.on( 'click', this.internalMethod ); this.trigger Used to emit events that originate from this controller (specifically, the scope element). this.trigger( [element elem,] string eventType [, object data] ); elem A reference to the element on which the event will be triggered. If not specified, this.scope is used by default. eventType The event being triggered. data An object containing data relevant to the event. this.triggerOn Used to emit events directly on sub-controllers from a parent controller. this.triggerOn( string controllerName, string eventType [, object data] ); controllerName The controller name to find and trigger the event on. The wildcard character (*) is supported at the end of this parameter and the event will be triggered on all matching controllers. Examples: core.front.core.comment core.front.core.* core.* eventType The event being triggered. data An object containing data relevant to the event.
  4. Note: This documentation assumes you are running your installation with IN_DEV enabled. Due to the multi-application model that the IPS Social Suite employs, and the way that resources are bundled and served to the browser, there are a number of places in which different types of javascript files are stored. Development & production When developing for IPS4, you create your javascript files in the application or global directories, with a single file holding a single module. In production, however, IPS4 bundles related files together (concatenating and minifying them in the process) to reduce file size and the number of HTTP requests needed to fetch the scripts. You should be aware of how the bundles are built and from where, because this will dictate how you specify javascript files to load in your backend PHP code. Global resources Global resources are those that are loaded on every page load. This includes: Libraries - jQuery, Underscore etc. Application setup - Bootstrap module, loader module, base controller etc. Global modules - UI widgets, utilities, global controllers etc. Front/admin modules - Controllers global to the front-end/admin control panel, needed on every page in that area. All of the above files are located at <root>/dev/js/. Application resources Applications have their own folder for javascript, located at <appName>/dev/js. Within this folder, there should be three subfolders - admin, front and global. These folders hold javascript specific to those areas (or for the whole application, in the case of global). Within each of those folders respectively, there are further subfolders for different types of javascript file - controllers, templates and mixins. Therefore, within an application, the file structure looks like this: /appName /dev /js /front /controllers /profile ips.profile.main.js ips.profile.body.js /templates ips.templates.profile.js /mixins /admin /controllers /templates /mixins /global /controllers /templates /mixins Bundles for an application are built based on folder/filenames, and use the format <location>_<directory>.js. Taking the structure above as our example, if we loaded front_profile.js, it would contain: /js/front/controllers/profile/ips.profile.body.js /js/front/controllers/profile/ips.profile.main.js /js/front/templates/ips.templates.profile.js This bundle would then be included in your PHP code like so: \IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front_profile.js', 'core' ) ); Third-party libraries An application has an interface directory at <appName>/interface/. This is where third-party libaries used by individual applications should be stored and loaded from. They are not bundled or minified by IPS4, so you should store the pre-minified rather than the uncompressed version. Assuming you had added a file to interface at <appName>/interface/jquery/jquery.rangyinputs.js, it can be included in your PHP code like so: \IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'jquery/jquery.rangyinputs.js', 'appName', 'interface' ) );
  5. Global scope & closure wrapping Variables must not be leaked into the global scope unless there is an explicit functional need for global access. Within function scope, variables must be declared with the var statement. To help prevent scope leaking, each script file should be entirely wrapped in an anonymous, self-executing closure, passing the jQuery and Underscore objects as parameters: ;( function($, _, undefined){ // Code goes here }(jQuery, _)); This prevents variables from leaking into the global scope, and also ensures jQuery code making use of the dollar variable $ does not conflict with any other code that might be running on the page. Note that a semi-colon appears as the first character. This helps prevent errors caused by incorrect semi-colon insertion in previous code, should this script be concatenated with others. It is a defensive coding practice, and recommended in all files. Strict mode The use of strict mode is required for all default javascript in IPS4. Strict mode helps prevent leaking into the global scope, catches code problems that would otherwise fail silently, and enables javascript engines to perform more optimizations. However, it is important that this is applied using the per-function syntax and not the per-script syntax, otherwise unexpected behavior can occur when script files are concatenated with 3rd-party scripts. Given that all script files should be wrapped in a self-executing closure, the syntax for enabling strict mode looks like this: ;( function($, _, undefined){ "use strict"; // Script here, e.g. module definition etc. }(jQuery, _)); By putting the "use strict"; statement inside the the closure, we ensure all of our code runs in strict mode without affecting any external code by accident. Note that the strict mode statement must be the very first line of the function, as shown. Further information about strict mode and the code requirements it imposes is available at MDN. Documentation & commenting The use of JSDoc is highly encouraged to document code. Refer to the JSDoc documentation for exact syntax. Within scripts, comment blocks /* */ should be reserved for formal documentation and situations where large blocks of existing code need to be commented out temporarily. Line-by-line comments should always use the single-line // comment style. /** * Formal documentation */ // Informal comments to describe code Semi-colons Semi-colons must be used at the end of every statement, even when function/object literals are being assigned. For example: // Simple statements $('#element').doSomething(); var test = 'value'; return 'text'; // Function and object literals var test = function () { ... }; var test2 = { key1: 'value' }; Names All names (variables, methods, properties etc.) should use lowerCamelCase, such as deselectAll, createModule and itemSelector. Logging You should not use console.log for debugging. This does not provide for disabling debugging in production, and can break pages on older browsers if not accounted for. A debugging method is provided via Debug.log that addresses these concerns, and should be used instead. Line length Aim to keep the line length below 120 characters to maintain readability. Longer lines should be split immediately after a comma or operator, with the remainder of the statement indented further to ensure clarity. For example: if( ( options.dataSource && options.dataSource.indexOf('#') === 0 && $( options.dataSource ).length ) || originalTextField.is('[list]') ){ jQuery chaining It is recommended that multiple jQuery methods chained on an element are placed on newlines to improve clarity. Additionally, indentation should be used to reflect the current working level of the chain. For example: $('selector') .find('.classname') .hide() .end() .find('.otherClassname') .css('width', '100%') .show() .end() .append( $('<div/>').attr( { id: 'someID' }); By indenting in this way, we make it clear that the hide() method applies only to the elements matching .classname, before calling end() to return to the original working level - and so on.
  6. In IPS4, most javascript is split into self-contained, named modules. Modules and javascript files have a one-to-one relationship - each javascript file should contain one module only. Modules prevent variables leaking into the global scope (which must be avoided), and helps ensure code stays focused. All modules appear under the ips namespace. Types of Module A number of primary types of module exist within the software: Utility Modules Exist on the ips.utils namespace, and provide assorted functionality that other modules can make use of, including cookie handling and methods for handling responsive design. UI widget modules Exist on the ips.ui namespace, and define interface widgets to add interactivity to pages, and which can be reused using special data attributes. Controllers Controllers are modules that handle specific functionality for one particular page or part of a page. They respond to events from UI widgets and other controllers and update their scope accordingly. Mixins Mixins are used to extend controllers with additional functionality. Other types There are a number of other types of non-modularized file used too: Libraries/interfaces Libraries are 3rd party scripts that provide additional functionality. jQuery and Underscore are two examples. Templates Template files define special Mustache templates, that modules can use when rendering HTML to the browser. Languages Language files define strings which can be translated into other languages.
  7. I want to briefly show our new cover photo support. Cover photos allow users to upload an image to represent something in the community; we currently support them in profiles and calendar events and may roll out support to other areas later. Here's a video of it in action for a calendar event. It's really simple to use, and of course still works responsively like the rest of our default theme. We hope it adds a new element of customization for content in your community. Developers For developers, supporting cover photos in your own addons is as easy as you'd expect. A helper is available which handles the nitty-gritty for you; you simply add $item->coverPhoto() to your template, override a couple of methods in your controller, and optionally build your own menu to control the user interaction (or you can let the helper output them for you, as in the video above). That's it! As always, screenshots are from pre-release software and are subject to change before release.
  8. We've previously shown how responsiveness works in the AdminCP, but I'd like to briefly introduce responsiveness on the front end, and pick a few views to show you as examples (this will be a screenshot-heavy entry!) What is responsiveness? Before we get to that, allow me to recap what responsiveness is. Responsive design is a method by which you design one page in such a way that it adapts for the available screen space on the device the user is using. This means that one theme handles both the full desktop view and the condensed mobile view with some clever CSS, in contrast to 3.x where we had a separate mobile skin. When we took the decision to use responsive design for IPS4, one key aim was to ensure that the mobile view isn't feature reduced. We want all functionality and all areas of the suite to be available regardless of device, and with only a couple of exceptions we're on track to deliver this. Primary navigation In mobile view, the primary navigation collapses and moves to a menu accessible with the icon in the top-right. The breadcrumb becomes a 'Back' control, taking you up a level from the current page: The primary navigation, when opened, looks like this: Moderation Given that the responsive theme supports all functionality, this naturally includes moderation. IPS4 support full moderation capabilities regardless of the device you're using. Here's an example of moderating images in Gallery. Notice the menu to quickly select types of content to moderate, as well as the floating toolbar at the bottom of the screen to choose actions. Settings page Taking the settings area as an example, here's the same screen at the three supported breakpoints - desktop, tablet and mobile. Profile view Here's profile view (which we covered in more detail here) as seen on a phone: Calendar Calendar views on mobile: Gallery Viewing albums & images in a category: Blog The blog homepage: And viewing a blog: Forums Submitting a topic on mobile: Conclusion So that wraps up this round-up of responsive views. Naturally, there's many more views than this in the suite and we can't show screenshots of every single one, but hopefully this entry has given you a taste of a variety of views, and a better idea of how we're approaching mobile users in IPS4. As always, screenshots are from pre-release software and are subject to change before release.
  9. Rikki posted a blog entry in Invision Community
    Profiles are one of the key sections of a community, as everyone knows. They are what represent your users; where their information is shown and their content is gathered. When users contribute quality content to your community, their profile is where other users go to find it in one place. In short, it's an important area. In IPS4, profiles have had a complete makeover. There's a lot to cover, so I'll start with a numbered screenshot, and address each section individually (please note this is a large image; if you're on mobile, you may wish to wait to view it full-size). 1 - Header images In 3.x, users could customize their profiles by uploading a background image. In practice, this didn't work well when the software was integrated into an existing website design, and the options presented often ended up with a garish profile. In addition, social networks like Facebook and Twitter have adjusted user expectations on how profiles are customized. In IPS4, instead of page backgrounds, users instead get to customize their profile header image. This provides the best of both worlds - ample space to choose something creative, but it's contained and won't mess up a website design. 2 - Reputation The user's current reputation count is shown prominently in the info column, letting other users know if this member is an asset to the community. 3 - Warnings For moderators/staff, the profile now provides quick access to warning tools. By expanding the panel, they can see a brief history of recent warnings: And clicking one of these pops up the warning details: New warnings can also be issued inline, of course. 4 - Followers Followers replace friends in IPS4, and the user's followers are shown in this block. Instead of requiring mutual acknowledgement as with the traditional friends system (an approach that isn't entirely useful in a community of anonymous users), in IPS4 you follow users whom you find interesting in order to be updated when they contribute to the community. Users can of course prevent others from following them, if that is a concern to them. We'll have more details on how followers works in a later entry. 5 - About the user Traditional information about the user is shown in the next block, including custom profile fields. 6 - Recent visitors Recent visitors to this user's profile are shown next. As with 3.x, this can be toggled on and off by the profile owner. In 4.x, this is done by clicking the X in the corner of the block. 7 - Follow/Message member These primary buttons enable others to follow the user (if enabled), and send a new message inline, without leaving the page. 8 - User's content In 3.x, browsing a user's content was handled by the search area of the community (though links were available in the user's profile and hovercard). We felt this wasn't the best place for it, though. After all, a user's content should be available in their profile. That's what this button does. It switches the profile view to 'content browsing' mode, where you can see everything the user has done. It's smooth and buttery, and because it all loads dynamically, it feels like a true part of the profile. Here's a video of it in action (14MB) 9 - Long-form custom profile fields IPS4 supports various kinds of custom profile fields, including rich-text editors for long, styled content. Those custom profile fields will be shown in the main section of the profile where they get the space they need to be effective. About Me is a default field, but you can of course add your own too for your users to fill in. 10 - User's 'Nodes' A node is a fancy developer term for content containers that a user creates themselves, like gallery albums and blogs (as opposed to forum categories, which are created by the admin). In IPS4, a user's 'nodes' are shown right on their profile page, making it easy to find more interesting content from the user. In this screenshot, you can see my profile is showing my albums, my blogs, and other blogs to which I contribute. For developers, supporting your application in this section is easy too. 11 - Status feed The status feed from 3.x is of course still present, and the interaction is all inline without leaving the page. Conclusion That's profiles in 4.0. We hope the new focus on content and streamlined design provides a better experience for your users! As always, screenshots are from pre-release software and are subject to change before release.
  10. IP.Board 3.x supports "My Media", which enables you to share other content from within the community by using the "My Media" button on the editor. This results in: http://community.invisionpower.com/files/file/4464-ips-gdk-for-ipboard-32-amp-33/ While this works, it has a few shortcomings: The styling of the block isn't really designed for each type of content it might show Users have to click the My Media button, then browse for the item, when they probably already know the URL they want to link to Not all content types are supported; e.g. you can't use My Media to link to a topic. For developers, implementing support for My Media in other applications was a process involving extension files and multiple methods We wanted to make sharing existing content much easier in IPS4, both for users and developers. "Embeddable content" is our solution. How to use it To embed content from elsewhere in the community, here's a step by step guide: Paste a link to it That's it! When you paste a link to almost any kind of content, whether it's a forum topic, calendar event, gallery album or more, IPS4 will automatically embed a small preview of the content, designed specifically for that content. In order to not disrupt an existing paragraph of text however, the embedded block won't be used if the link is surrounded by text. Embedded content only shows if the link is pasted on its own line, giving users more control over their post. Here's what a post looks like with a few embedded types shown: Embedded content can be used anywhere as you'd expect, including posts and comments, but also status updates, IP.Content articles, and so on. For developers Supporting embedded content in your apps is very easy; your content model simply has to implement IPSContentEmbeddable: class _Topic extends IPSContentItem implements ... IPSContentEmbeddable Your controller then simply looks for an embed request and returns HTML - that's it. Our default blocks also have their own template and CSS file, so theme designers can change the styling on a per-theme basis. Conclusion Our hope is that this easier method of embedding content encourages more cross-posting and highlighting of good content in IPS4. The process is almost wholly automatic, meaning users don't have to think in order to share great content with others. As always, screenshots are from pre-release software and are subject to change before release.
  11. Rikki posted a blog entry in Invision Community
    One of the most distinctive uses for a forum is that of a 'knowledge community', where users visit in order to get help with a problem or question. Our own Pre-sales forum uses this model, but we also have many customers who run forums that are almost exclusively knowledge-based (such as Roxio and Evernote). IP.Board 3.x introduced the concept of a "Best Answer" flag, allowing topic creators and staff the ability to highlight the reply to a topic that they deem best answers the question. This shows a snippet of the post in green at the top of the topic. Many sites now use this feature, but for IPS4 we wanted to expand the functionality offered for these types of forums. Question & Answer Forums Forums in IPS4 will enable you to set a forum as a "Q&A Forum". This adjusts the forum to be specifically designed for knowledge sharing. Instead of topics and posts, it has questions and answers. On the forum index, the forum will be shown as a Q&A forum with its forum icon (unless you've set a custom forum icon for that forum): Forum View When you enter the forum, instead of the normal topic listing, you see a list of questions: You'll see here that questions that have a best answer are indicated with a green checkbox. You'll also notice that one of the stats on the right hand side is 'votes'. In Q&A forums, questions can be voted up or down by users, in order to give them more visibility. More popular questions will bubble to the top (depending on the age of the question). You can of course still order by more traditional methods, if you wish. Popular questions from the past 30 days are also highlighted at the top of the forum, providing an up-to-date 'knowledgebase' that other users can see. Using our own presales forum as an example, if someone asked a question about an important feature and it was voted highly, other users visiting the forum would see it right at the top, which is great for content visibility and helping users get the answers they're looking for with minimal fuss. Question View Clicking into a question shows an adjusted topic view: The question (i.e. the first post) is shown at the top of the page on all pages, with answers listed below. You'll see that replies can also be voted up and down - in fact, this determines the order in which answers are shown inside the question. Popular answers, as determined by the community, will appear at the top, with worse or incorrect answers being pushed down. This is great for quickly finding the best information for the question at hand; in IP.Board 3.x, all too often a high-quality answer will appear in the middle of a topic and unfortunately go unnoticed by the topic creator or others looking for an answer. You can still sort answers by date, if you prefer. In the screenshot above you can also see the first post is marked as the best answer. "Best Answer" always appears at the top, regardless of its vote count. Question/answer ratings are separate from reputation, so you can of course still "Like" posts even if you don't think they're a good answer to the question. Conclusion So that's the new Q&A feature for IP.Board. We think it'll a big step forward for knowledge-driven communities using IP.Board, or even individual forums in other communities (like our pre-sales forum), helping users find answers to their questions more efficiently, and ultimately making your communities more useful. As always, screenshots are from pre-release software and are subject to change before release.
  12. Different staff members typically have different roles within a community - especially larger communities, where you may have staff responsible for the theme, others handling tickets and different staff maintaining the system. In 3.x, we had a 'Bookmarks' system in the AdminCP that allowed you to create a menu of frequently-used sections in an effort to make them easier to get to, rather than navigating the main menus. As with every feature in IPS4, we took some time to think about what this Bookmark feature aimed to achieve, and whether it was the best way to achieve it (seriously - we have considered everything you'll see in IPS4 very carefully; nothing gets a free pass). We determined through speaking to administrators that the primary use of this feature was actually to get to one place quickly - whatever place that might be. It appeared to be rarely used as an actual bookmarks menu, and besides, duplicating browser functionality should always send up a red flag. We decided to rethink the idea. What we decided to do instead is allow AdminCP menus, both primary and secondary, to be reordered on a per-admin basis. This means each admin can set the AdminCP menu up however works best for themselves. If you use the theme system a lot, you can make that your first menu item. Or, if you use the ticket system in Nexus, you can put that first. Here's how it works:
  13. The submissions process in IP.Downloads has a certain complexity that may not be apparent at first. As well as simple file uploads, we also support adding files from URLs and from the local file system, and screenshots can also be added in these ways. Which category you choose to submit to affects which of these options are available. In addition, via the AdminCP you can bulk-upload files - but not via the front-end. For IP.Downloads 4, we wanted to improve this process with interface and functionality changes. Submitting Files Here's a video demonstration of how creating a single file record in IP.Downloads works in v4: We've worked hard to improve the flow for users here - while they are uploading files (which may be large and take some time), they can continue adding the other file information such as screenshots and meta data. While that's happening, the upload progress is always shown at the top of the screen. In the video you'll also see how image uploading is handled, as well as prefixes in the tag system, which are now supported throughout the IPS Community Suite. Bulk Uploading Instead of going to the AdminCP to bulk-submit files, single- and bulk-uploads are now handled through exactly the same process on the front end. This means users can be granted bulk-upload permissions without requiring AdminCP access, a big improvement on the current implementation. To bulk upload, a user clicks the "Submit a file" button as normal, and chooses "I want to submit multiple files at once". They see the same upload area, but this time, the file information step is handled separately after the page is submitted. Each uploaded file has a separate block for file information and its own set of screenshots. We'll of course be showing more of the IP.Downloads homepage and file view later, but we hope that gives you a taste of what to expect in IP.Downloads in IPS4.
  14. Reminder: this blog covers the technical details of 4.0's programming. For details on 4.0's features, follow our main blog. Reviewing controllers Some time ago, I blogged about the javascript framework we've built for IPS4. In it, I covered the most important component: controllers. To recap, a controller is a special object within the framework, and is applied on specific elements. That element is the controller's scope, and the controller works on it to provide its functionality. For example, a simple controller might look like this: ips.controller.register('core.global.core.example', { initialize: function () { this.on( 'click', this.showAlert ); }, showAlert: function (e) { alert( "This button's text is: " + this.scope.html() ); } }); It would be used on an element like so: <button data-controller='core.global.core.example'>Click me</button> Here we're registering core.global.core.example as a controller. This represents the controller path - it's in the format app.module.group.controllerName. Though it seems longwinded, this allows IPS4 to dynamically load controllers on-demand, rather than loading them all when the page is loaded. In the initialize method (called automatically), we set up an event handler for a click. When the element is clicked, you'd see an alert saying "This button's text is: Click me". So, that's how controllers work. Almost all page behavior in IPS4 is handled through controllers. But how would you change a method in an existing controller, say if you were writing an addon, or if you had two controllers that were fairly similar, and wanted to provide a base controller they both shared? Mixins To enable that, we have mixins. Mixins allow you to specify functions which are inherited by objects - in this case, our controller objects. This means, using mixins we can add new functions to a controller without needing to edit the controller itself. A mixin is defined like so: ips.controller.mixin('addAnotherMethod', 'core.global.core.example', true, function () { this.anotherMethod = function () { alert('Inside anotherMethod'); }; }); The ips.controller.mixin method takes up to 4 parameters: ips.controller.mixin( name, controller, automaticallyExtend, fnDefinition ) name: Name to identify this mixin controller: The controller this mixin extends automaticallyExtend: [optional, default false] Does this mixin automatically extend the controller (more on that below) fnDetinition: The function definition applied to the controller In this example, our mixin adds a method named anotherMethod to our core.global.core.example controller shown earlier. Let's talk more about the automaticallyExtend parameter. Mixins can be applied to controllers in one of two ways - either automatically on a global basis, or manually on a case-by-case basis. Mixins are manually specified like so: <button data-controller='core.global.core.example( addAnotherMethod )'>Click me</button> This means the mixin is used on this element - but another element using core.global.core.example wouldn't get it. This is useful when you're building your own apps or addons; you can write simple controllers that implement base functionality, then extend them with functionality for specific cases by specifying the mixin name in your HTML. We use this ourselves - for example, we have a base table controller that handles sorting, filtering and so forth. We then have a mixin for AdminCP tables, and a mixin for front-end tables, which add functionality specific to those areas, reducing code duplication. If you're extending an IPS controller in an IPS app, though, modifying the HTML isn't typically an option. Instead, you can specify the mixin as global, and it will be applied to all elements where that controller is used. This means you can write your own mixins that work with our default controllers without having to touch our controller code (and that's a good thing). Advice So that shows how to add new methods to a controller. But what if you want to work with the methods that already exist in the controller? In the above example you'd only be able to overwrite an existing method - certainly not ideal, because 1) you would break any other mixins using the default method, 2) if we made an update to a method in a later release, your mixin would break it. To facilitate working with existing controller methods, we're using a model called advice. This adds three special methods to a mixin: before, after and around. These let you 'hook into' existing methods and provide additional code for them. Let's rewrite our example mixin from above to take advantage of it: ips.controller.mixin('changeBackground', 'core.global.core.example', function () { this.before('showAlert', function () { this.scope.css({ background: 'red' }); }; }); Here, I'm using the before method. I'm hooking into showAlert method (from the controller), and changing the background color of the scope element. So what happens when the link is clicked? First the background changes to red, and then an alert box is shown. We've added the background changing functionality without needing to edit the controller at all. Here's two other ways of doing the same thing, using the other two special methods: ips.controller.mixin('changeBackground', 'core.global.core.example', function () { this.after('showAlert', function () { this.scope.css({ background: 'red' }); }; this.around('showAlert', function (origFn) { this.scope.css({ background: 'red' }); origFn(); }; }); The after method is fairly self-evident. With the around method, the original function is passed in as an argument, allowing you to determine when it is executed by your mixin. All three of these methods will stack, so multiple mixins can hook into the same method, and they'll be executed in order, each receiving the previous. Conclusion I hope this introduction to mixins proves useful to developers; it shows how our core app controllers can be extended in a non-destructive way, but also how your own apps can use the mixin functionality to create an inheritance model to make your life easier. Javascript in IPS4 makes extensive use of custom events, so the preferred way of adding new functionality is to listen for appropriate events and act on them - but the mixin support described above provides a mechanism by which you can adapt existing event handlers.
  15. In 3.x, we support HTML emails being sent by the software. However, due to constraints we had at the time, HTML emails use pretty much the same content as plain text emails, but wrapped in a simple HTML wrapper. Additionally, users had to explicitly decide whether they wanted to receive HTML or plain text emails via a preference setting - quite an anachronism. All in all, not a very satisfactory user experience. Email handling in 4.0 In 4.0, users no longer choose which type of email to receive. Our email handler sends both types in a single email, and the email client chooses the most appropriate to show based on its capabilities. If it can display a fancy HTML version, that's what they'll see by default, but plain text is used if not. Email template system In 3.x, email content is defined by the language system, and each email has one language string which forms the content for both the plain text and HTML versions. Clearly, if we were going to improve the HTML templates we ship with, this would have to change. In 4.x, each type of email has two templates - one for HTML, one for plain text. This means a better display of content can be created for HTML emails, while keeping the plain text ones simple and to the point. Email templates make use of the skinning system foundation (which we'll reveal later), meaning they have full use of logic, template tags and more - so we can also customize the emails depending on the user they are being sent to (note though that email templates are not per-skin; they are global to the site). And, of course, email templates can be added and edited via an interface in the AdminCP. This isn't groundbreaking stuff, but a vast improvement on email handling in 3.x. Email template design We also wanted to improve our email templates, so that each type of email sent was designed specifically for the purpose. The data shown in a registration email will be different to a topic digest, for example, and the email should reflect that. Coding email templates is not a trivial thing, unfortunately. The latest version of Microsoft Outlook uses the Microsoft Word rendering engine(!!), while GMail strips out all CSS included in style tags - and that's just the start of the gotchas. This makes designing email templates a tricky business, and one that requires lots of testing to ensure compatibility. For our first 10 templates alone, I reviewed 900 screenshots to spot problems. As a result, we've taken the approach of creating email templates which are simple in appearance and would work well for most sites, with the goal of hopefully avoiding the need of most sites to edit them at all (though you can, if you wish). The colors we've used are fairly neutral, for this reason. For those mail agents that are a little more... advanced, our email templates in 4.0 will be responsive. They will look great on mobile devices as well as desktop clients. I have included some examples of email templates, along with their mobile counterparts. I should note at this point that this does not reveal the main skin design. As discussed above, emails are intentionally separate in design. Admin-completed registration Friend request New personal message New profile comment
  16. We've been hard at work on IPS 4.0 for some time now, and we're finally at a stage where we are ready to reveal the new AdminCP to you. I won't be showing you everything the ACP has to offer - some things will be revealed in more detail in later blog entries. But lets get to an overview. Background information IPS4 brings with it a new CSS framework that aims to modularize our styles. This is something we started to work towards in IPB 3.2, but at that time we couldn't completely replace our structure. We no longer have a monolithic ipb_styles.css file. We now have a bunch of small CSS files, and each one handles something in particular. There's one each for forms, tables, pagination, buttons, layout and so on. This brings a few key benefits. Firstly, when we need to make a bug fix in, say, the forms CSS file, IPS4 will still be able to automatically upgrade all the other css files for you. In 3.x, one bug fix in ipb_styles.css could mean the whole file had to be manually upgraded. Secondly, it will be a lot more obvious for skinners where to look for particular things. Need to style a button? Look like buttons.css. Easy. And thirdly, if you're building pages in IP.Content, and you want to use our button styles, you can simply include that one CSS file without needing to include the entire CSS framework. CSS is of course concatenated and compressed before being delivered to the browser, but in a development environment, it exists as I described it above. In IPS4, both front end and AdminCP share the same CSS (and Javascript) framework. Skinners will be able to ship skins that work on both the front end and AdminCP with only a little extra work - and, of course, when we make bug fixes to the framework, it'll fix both areas. Before we go further, I want to make this part clear: The front-end and AdminCP look different. What you'll see shortly isn't what the front-end looks like. We will reveal that separately later. While the same framework is used, the AdminCP extends and overrides parts of it to suit its needs and style. Goals What did we want to achieve with the AdminCP? Our current AdminCP is often regarded as the best out of the big forum software platforms, so redesigning is a big undertaking. Better user of space. Our current ACP uses vertical space for the main menu, and horizontal space for the application menu. In an era of widescreen desktops being standard, this could be improved. Get rid of dropdown menus. The main menu currently uses dropdowns for navigation, but this can be difficult to use - especially if you want access something in a 3rd party app, meaning you have to traverse the Other Apps menu. More consistency across pages. Our current ACP has some interactive tables (e.g. the member list) - but not every table makes use of the functionality. We should be enhancing every page with similar functionality, if it makes sense. Better styling. People aren't a fan of pink, it turns out. I guess it'll have to go. The blue gradients are showing their age too. And the big one: Better mobile support. You can't effectively use the AdminCP on a mobile device. It's time you were able to manage your entire community from your phone with all of the same functionality, right? Responsive by default That last one is what we're most excited about. The AdminCP in IPS4 is fully responsive, and allows you to do everything just on a phone or tablet. What is responsiveness? It means that the page automatically changes to better suit the device you're using. While a desktop user would see full navigation menus and tables of data, a mobile user will see a reduced view (but with all the same data present!). Whether you need to manage your members, change some settings, send a bulk email or run some diagnostics, it can all be done on the go. This is a first for the big community software platforms, as far as I'm aware. Preview Here is a sample page from the new AdminCP, as seen on a desktop, with the same page shown at a mobile resolution: Although I won't include it here, tablets will see an 'intermediate' view with a reduced menu on the left. So, let's go over some of the key features of the screenshots. Navigation First, and perhaps most importantly, is the navigation. On a desktop, your applications are now arranged down the left-hand side, with their respective section menus available simply by hovering on the application - no dropdown menus to traverse. The application menu can be reordered per-admin, allowing each staff member to set the menu up to best suit their role. On a mobile, there's obviously not the space for a wide navigation menu. Therefore, the application/module menu is activated by clicking the top-right icon. This opens a sidebar, from which you can navigate: Tables What you see in the screenshots are our new default way of displaying tables of data. On the desktop view, we have filters across the top, a search box (and advanced search popup), and table headers can be clicked to dynamically sort the data via ajax. On a mobile view, this all collapses down - filters and sorting become menus, while table rows collapse to show data in a more suitable view. Responsive tables are a tricky thing to do right and there's a few different approaches, but given the types of data our AdminCP tables typically show, we think this is the best approach for us. Forms As has been discussed in some of our developer blogs, the IPS 4.0 framework supports a wide range of form field types - everything from text inputs to tree selectors to matrices. All of these field types work both on desktop and with a responsive mobile view. Here's a simple AdminCP form on both desktop and mobile: Tabs Tabs are used extensively, where appropriate. Here's a screenshot showing a typical tabbed page (and it also shows a tree view): Video of the mobile view in action I've taken a short video of the member section in action, showing filtering, live searching and the advanced search popup. I'm using the iOS simulator here, which has some display jitters and requires me to use the mouse, but it should give you a good idea of how the AdminCP will work on a phone. Conclusion So there we go - an overview of the new AdminCP. We still have more to show you. Individual features and pages that are noteworthy will be blogged about in due course in more detail, so keep an eye on this blog and our developer blog for more. Please do bear in mind that this is pre-alpha software, and everything you see is subject to change. We look forward to your feedback!
  17. Javascript is a key component of front-end web development - it's essential for a modern web app to provide a good user experience, and javascript is central to enabling that. Getting it right in 4.0 has been one of our goals from the start. Problems To begin, let's summarize some of the issues with javascript in the current 3.x series: Lack of file organization (a single JS directory that contains dozens of unrelated script files) Different script types are bundled together (interface widgets, routes and full applications all in one place) Lack of modularity (each JS file is pretty much a floating island in how it might implement functionality, with no formalized structure) Simple things requiring code (how many times have you had to write half a dozen lines of JS just to launch a popup?) New dom nodes aren't initialized automatically (load some new HTML via ajax, and your popups won't work without manually hooking them up) Resolving these problems informed the javascript framework that we've built for 4.0. It all ultimately comes down to organizing code better and making the lives of developers easier (ours and yours!). The solution Our solution has been to build a framework which is modularized and heavily event-driven. In most cases, modules never call each other directly, but instead communicate with events which may (or may not) be responded to by another module. More on this later. The framework breaks down into four types of module: Widgets - interface widgets like popups, tooltips and menus Utility modules - things like cookie handling or URL manipulation Controllers - modules which work on a particular dom node. More on these later. Models - modules which handle data. In the vast majority of cases this is simply fetching data from the server. There's also 'interfaces', which are third party scripts like CKEditor, jQuery plugins and so forth. They aren't part of the framework, so I won't discuss them here. The groundwork Before getting to specific types of modules, we needed to lay the groundwork. Javascript 4.0 is modularized, with only a single global variable (ips) being created in the page. All other scripts are defined as modules, whether they are interface widgets, utilities or anything else. A module is defined as a function which returns an object containing public methods (the revealing module pattern, if you're interested). Here's an example module: ;( function($, _, undefined){ "use strict"; ips.createModule('ips.myModule', function () { // Private methods var _privateMethod = function () { }; // Functions that become public methods var init = function () { }, publicMethod = function () { }; // Expose public methods return { init: init, publicMethod: publicMethod } }); }(jQuery, _)); This pattern works well for our purpose, because it enables a module to contain private methods for doing internal work, while exposing only those methods which should be public to the outside world. This example module could then be used like so: ips.myModule.publicMethod(); So this keeps everything neatly organized, and ensures variables don't leak into the global scope (which we want to avoid at all costs). When the document is ready, the module is automatically initialized (though you can also have functions that execute before DOMReady if necessary). Interface widgets It's fair to say that interface widgets make up a large proportion of the JS used in our software - a web app such as ours has an intrinsic need for popups, menus, tooltips and so on. As mentioned above though, the big hinderance in 3.x is that these widgets have to be created manually (or, in a few simple cases, a special classname is added to an element to initialize it). This feels unnecessary when all the developer wants to do is show a simple widget that is otherwise standard. To alleviate this hassle, interface widgets in 4.0 all support a data API. What this means is that any widget can be created simply by adding some parameters to an HTML element, and specifying some options. Need a dialog box that loads a remote page? Simply do: <a href='...' data-ipsDialog data-ipsDialog-title='My dialog' data-ipsDialog-url='http://...'>Click me to open a dialog</a> Or if you need a hovercard, just do: <a href='...' data-ipsHover>This will launch a hovercard</a> We already have around two dozen widgets built, covering everything from dialogs, menus and tooltips, to keyboard navigation, tab bars and autocomplete - all supporting initialization with data attributes. Building your own widgets is easy - they are built as a module, and then simply define some settings to be accepted. The widget module does the rest. They can either be initialized when they're first seen in the dom (which you'd want for something like an image slider widget), or when an event occurs (such as hovering on a link, in the case of hovercards). Whenever new content is loaded into the page, widgets will be found and initialized automatically, too. Most widgets emit events when certain things occur - when we get to Controllers, you'll see why that is useful. Utilities Utilities are simple modules that don't need much discussion. They simply provide methods which do something useful - for example, fetch/set a cookie, write to the user's local browser database, or handle timestamps. Controllers Controllers are the meat of the application. Whereas interface widgets are used (and reused) as dumb tools on a page, controllers provide the logic for particular elements, sections and pages. They would, for example, handle the interactions in the topic listing, or the interactions with a post. Notice the word interaction - controllers are specifically designed to deal with events on the page. In fact, that's almost all they do! Controllers are initialized by specifying the controller name on an element, like so: <div id='topic_list' data-controller='forums.topicList'> </div> This div becomes the controller's scope. The controller can manipulate content inside the div, watch for events, and so on. Controllers, in general, should be as specific and succinct - so simply specifying a page-wide controller then handling everything inside it is discouraged. If we take the topic list in forum view as an example: <div id='topic_list' data-controller='forums.topicList'> <ul> <li data-controller='forums.topicRow'> ... </li> <li data-controller='forums.topicRow'> ... </li> <li data-controller='forums.topicRow'> ... </li> </ul> </div> Each topic row might specify the forums.topicRow controller which handles locking, pinning, or marking that topic. The topic list itself might specify the forums.topicList controller, which handles sorting and loading more topics. By doing it this way, controllers become responsible only for a specific portion of the functionality, which keeps them lean and simple. Controllers are entirely decoupled and cannot reference each other - which is by design, given that a controller is only interested and responsible for its own scope. To communicate, events are used. A controller can trigger events on the page, which other controllers, widgets and models might respond to (and in turn emit their own events). Continuing the example above, let's assume one of our topic rows is being deleted. The forums.topicRow controller handles removing the HTML from the DOM, but it doesn't care what happens after that - it's not its responsibility. However, it emits a deletedTopic event to let the page know. The forums.topicList controller sees this event, and because it does care, it loads a new topic entry into the list. By using events like this, we can build interfaces that respond fluidly to user interactions while still maintaining separation of concerns. So, how does a controller deal with events? Because we're using jQuery, event handling in controllers piggy-backs with the on and trigger methods. In the controller's initialize method (which is specifically for setting up event handlers), you simply do: this.on( 'menuItemSelected', '#menuid', this.handleMenuClick ); Usually when setting up events in an object using jQuery, you need to use $.proxy to properly control the scope of this, but in controllers, this is handled for you automatically - you just specify the method name. Notice the event we're observing here - menuItemSelected. This is an event that the ui.menu widget emits, and it illustrates how widgets and controllers can interact. Controllers can watch for events from widgets, then do something with the information given, all without ever directly referring to each other. Triggering an event is similar: this.trigger( 'doSomething', { color: 'yellow', size: 'big' }); This is the same syntax as jQuery's own trigger, except that the controller will ensure the parameters object is passed between different event handlers in the same chain. This will hopefully be clearer when you get your hands on it. Models Models are quite similar to controllers (they also use the special on and trigger methods), but their only purpose is to handle data. By decoupling data handling from controllers, we can centralize data getting/setting so that any controller can use it. Let's say we have a user model, which handles data for the current user. It might have event handlers for adding a friend, for example, so when it sees the event addFriend, it handles it appropriately. Let's also assume we have a controller on each post in a topic, there's three posts, and that the controllers are observing the click event on the 'add friend' button. Here's the sequence of events: (controller1) click 'add friend button' (controller1) emit 'addFriend' event (user model) adds a friend via ajax (user model) emits 'addedFriend' event (controller1) updates friend icon (controller2) updates friend icon (controller3) updates friend icon Even though it was controller1 that requested that the model adds a friend, all controllers respond to the event the model emits and updates the friend icon in its own post. This again shows the power of using events as the primary communication system - anyone can respond, and the caller doesn't have to deal with maintaining associations. Conclusion So that's about it - the new JS framework in IPS4. Hopefully this in-depth post has covered everything you need to know at this stage. You'll be pleased to know that most of the framework and widgets are already documented, and that will be available when IPS4 hits beta. Do note that everything covered here is subject to change or removal, as usual in our development blogs. If you have any questions, feel free to ask!
  18. I'd like to introduce two new areas we've been working on. These new areas are designed to support our developer community, while making it easier for our clients to get their custom projects taken care of. Projects http://community.invisionpower.com/resources/projects The first new area is Projects. When you have a custom project for which you need a developer/designer, this new area will allow you to gather responses from developers interested in working with you. Post your project details, choose an approximate budget and date, if applicable, and developers can then register their interest in the project. From there, you can contact developers to help you decide which to go with. Projects you post will be open for responses for 30 days, after which they'll be closed. If you agree to work with a developer before that time, you can mark it as completed by clicking the Accept link next to the chosen developer's response. You'll be notified when new developers respond to your project, too. IPS won't be involved in the communication between you and a developer in any way, so it's up to you to agree project details and exchange payment, if necessary, before work starts. We hope this will become a handy tool to help match up customers and developers on custom projects. In time we'll add more features, such as the ability to review customers/developers when a project is complete. Developer Profiles http://community.invisionpower.com/resources/developers The second new area is Developer Profiles. This new area gives developers a place to present themselves to potential customers. When a developer creates a profile, we'll automatically build a page for them that pulls in their Marketplace information, and gives them a way of highlighting one of their contributed files and a place to write about themselves. As a customer, you can browse the listings to check out the developers in our community. If you've worked with a developer or just like their files, you can Recommend them by clicking the link on their page. If a developer with a profile responds to a Project, we'll link their name to their developer profile so that you can find out more about them. We hope these two new features will help foster more growth in the development community, making it easier for customers to find developers, and giving developers a central place to find potential work. Check them out - and if you have any feedback, please do feel free to share it. We've already made many changes based on the feedback from our preview to developers, so now it's your turn :smile:
  19. As part of our goal to improve the usability of IP.Content, particularly for non-coder administrators, we've used the 2.3 release to look at ways of reducing or eliminating the amount of code that needs to be written in order to create great websites. In the current version of IP.Content, blocks require individual templates (entered as part of the wizard process), and the default, unedited HTML is generic and not ideal for most blocks you'd want to create. This clearly left room for improvement, so we're pleased to introduce our solution: block templates. Block templates give us two primary benefits: Make it easier for IP.Content users to set up a good website, without needing to code Make it possible for those who can code to share interesting block interfaces either directly or through the IPS Marketplace Reusable Block Templates In IP.Content 2.3, the templates that feed blocks use are created separately, using a new Block Template manager, and then assigned to blocks when they are created: The block template manager What does this mean in practice? Firstly, it means that block templates can be used by more than one block, without recoding them, and when changes are made to the template, all blocks update at once. More importantly, it means block templates can be created, exported, shared and imported, so that those with the skills to create them can share them with those who can't. We hope this will spur some great block interfaces from our modding community, to benefit all IP.Content users. How does it work? Templates are linked to a feed type and a content type. For example, you might create a template designed for "Topics" in the "Forums" feeds, or "Albums" in "Gallery". In addition, you can create generic templates based on a content type alone, for example "Comments", which can then be used with any feed that shows comments. Adding a new block template When feed blocks are created, you are presented with a gallery of all suitable templates. Clicking one previews the template with the real data that the block will generate, so you can see exactly what the block will look like on your pages: Creating a new feed block using a block template Of course, if you still wish to provide a custom template for a particular block, you can do so - the process will be exactly the same as before. A Javascript & CSS framework provided To go with block templates, we've created a mini Javascript and CSS framework. This means that all block templates, whether they are provided by us or by our modification community, can maintain a consistent style. The required framework files are automatically injected into all IP.Content pages, whether you use the IP.Board wrapper or your own HTML wrapper. The javascript side of things is powered by jQuery, and based on the jQuery UI Widget Factory. Block templates can include their own assets, such as javascript widgets, CSS files and images. IP.Content automatically compresses all the JS and CSS files from all templates into a single minified file to help keep websites loading quickly and cut down on HTTP requests. Sharing templates As mentioned above, block templates can be exported and shared with other IP.Content users. To make this process very simple, when a template is exported, it's built as an XMLArchive - that means all of your CSS, JS and other assets are compiled into one file for you to distribute (a little like an IP.Board skin is). Exporting a block template into one XML file Conclusion To give you a taste of what is possible with block templates, here's a demo page I've created using some of the default templates we'll be providing. No code - other that putting template tags into a page and adding a div to make the right column - was typed to build this page! An example page using default templates Our aim with block templates is to allow all IP.Content administrators to have interesting, useful, interactive blocks, without needing to write the code themselves. The addition of a new standardized JS and CSS framework also means that blocks can maintain consistent styling, regardless of who authored the template. We'll be including a number of pre-made templates with IP.Content 2.3, and hope that the modification community and other coders will embrace the new possibilities offered by block templates, and create many more to be shared via the IPS Marketplace. Once IP.Content 2.3 is released, we'll publish some developer documentation to help you get started with the new tools. We think block templates are a huge step forward in making it easier to create websites using IP.Content. We hope you exploit their full potential! We look forward to your suggestions and ideas for improving the software in our feedback forum. We heavily rely on feedback from all of our clients to shape the future of our software, and even if we don't reply to every topic, rest assured they are all read. If you have any comments on the new block templates feature in IP.Content 2.3, please share your thoughts below!
  20. We've had specific support for mobile touch devices in the IP.Board suite for a number of versions - these devices see a specially designed skin which is stripped down and touch friendly, rather than the usual skin. We've taken the opportunity to update this skin for IP.Board 3.2, refreshing some aspects of the style and adding support for new features. Launcher Gone is the linear menu of community links, replaced instead with a new launcher screen that shows the various areas that can be accessed: As well as looking more pleasant, the increased space for each item should make tapping easier. In addition, you'll notice that we now display bubbles that show the message/notification counts, like on the default skin. General interface improvements We've restyled most elements in the skin, to lighten it and reduce the space necessary. As an example, here's the topic list: You'll notice the navigation bar that used to be present is now displayed as a 'Back' link in the header bar - a convention mobile users are well used to. The individual page title is shown in the black bar, with the page content following. In this particular screenshot, you might also notice that we're using the standard default skin read/unread topic markers, since they're perfectly suited. Here's a look at the slightly-updated topic view: As well as the cleaner display, you might notice we also support the new Like feature for posts. Notifications Notifications aren't something we've supported in the mobile skin before, but with more focus being placed on them in IP.Board as a whole, we took the step of adding them to the mobile view too. They essentially work just like you're used to, with the option to go to the user, the content, of the individual post: Tagging Tagging is a new feature introduced in IP.Board 3.2, and since tags can be set as required when posted, it was essential that posting support was added to the mobile skin. This series of screenshots shows the process: To remove a tag, simply tap it, then tap the Remove Selected button that will appear: If you choose to use the closed tagging system (whereby users can only choose from tags you define), a select list is shown instead, allowing users to choose from the native phone interface. View New Content/Searching IP.Board 3.2 introduces a new way of filtering results when using View New Content or when searching. A bar is shown on the left, with the various filter choices able to toggled on and off to change the results. While this interface clearly wouldn't work on a mobile device, we did want to ensure the filtering options were still available. This is what we came up with: Clicking one of the buttons on the top bar presents the available filters, with the currently-used shown with a check:
  21. Back in February, we posted a series of blog entries about improvements to our Calendar application (part 1 2 3 4 5). To go along with these technical changes and improvements, we've completely overhauled the calendar interface to modernize it and improve usability. This blog entry will review those changes so you know what you can expect when IP.Board 3.2 is available. Main Calendar View When first visiting the calendar, you're presented with a refreshed calendar view. The main calendar now takes up the full width of the page, since it's the most important content on the page. Mini calendars now appear below - available, but not hogging space. The new filter bar above the calendar allows you to switch seamlessly between month, week and day view. The menu next to the page title allows you to switch the calendar you're currently viewing. Week View Week view hasn't changed substantially, but does continue the filter bar and mini calendar styling introduced on the main page. In both the month and week views, you can double click a date as a quick shortcut to viewing that day. Day View Day view presents a much cleaner overview of events and birthdays taking place. In addition, a quick overview of members who have RSVP'd to an event are shown. Event View The redesigned event view shows full information about the event, together with comments and the RSVP list. We hope these improvements to the Calendar application go down well with your community, and spark more use by members! The new interface together with new features and improvements have given the Calendar a completely fresh feel, and we hope you like it!
  22. In this entry, I'll be showing the new streamlined Sign In and Registration process in IP.Board 3.2. Registration in particular is a key interface in any community. Too complex, and users will be put off signing up and taking part, potentially costing your community dearly. We've known for some time that our current registration process has needed streamlining to improve user conversions, and IP.Board 3.2 has proven the ideal time to implement the changes. Step by Step Our registration routine (when IP.Nexus is used) is unusual in that it behaves as both a store and a sign up process (to enable users to select a subscription). While not everyone uses IP.Nexus, it's important for us to consider this aspect and find a suitable solution regardless of the products used. To solve that, we've changed the registration routine to be a clear step-by-step process. First, a subscription is chosen if available. Next, account details are entered. Finally, the user gets confirmation. A step-by-step interface gives users clear expectations of the process they're about to follow, which is great for usability. Reducing clutter and confusion The current registration routine involves a number of disparate steps, collecting information that doesn't warrant an entire screen individually. This simply puts obstacles in the way of users trying to join. The account details step now consists of just one screen. COPPA, terms and conditions and of course account information is all covered on the one screen. COPPA, if enabled, appears as a modal popup. Instead of asking users to select their birthday (used only to verify they were older than 13), we now just ask the simple question: "Are you over 13?". Easy. Once they confirm, the popup is dismissed instantly, and the form is ready for them to begin. The registration form now consists of just 4 fields (6 if you use the Q&A and ReCaptcha features). So what's changed? Display Name - we no longer show the display name field upon registration. Requiring users to choose two names invites confusion, and the difference is not apparent until the user is familiar with the community. But fear not, the display name feature hasn't disappeared. Instead, we set the display name to be the same as the log in name during registration, but users are free to change their display name later in their UserCP. Confirm Email - There's no need to confirm the email address field. It's plain text, and many users simply copy and paste the first field anyway, duplicating rather than fixing an error. Time Zone - Having the user choose their timezone simply adds to the burden of registering. Instead, we now try and determine their timezone automatically behind the scenes, and it can always be changed in the UserCP. Another change is that the community terms and conditions are no longer presented on an entire screen. While important, they again present a roadblock to getting a user registered. Instead, they're now shown in an inline popup, and the user simply checks a checkbox to signify their agreement. Custom profile fields set to 'show on registration' also appear on this page as usual. However, we urge you to consider whether a particular field is essential at registration! More fields equals more work for users, so the fewer the better! Inline Sign In Something that has been requested for some time is inline sign in, and I'm pleased to say it's now available in 3.2. To simplify the process, users signing in with 3rd party systems such as OpenID will still be required to use the full sign in page. In our experience, though, the vast majority of users use the default IP.Board sign in, and the new inline popup should make the process a little easier for them. We hope you find the changes give a boost to your community when IP.Board 3.2 is available. By streamlining registration and sign in, we hope to encourage your guests to convert to members and contribute to your community.
  23. We've gradually been revealing aspects of the new IP.Board 3.2 front-end interface, and naturally there's much more to come. In this entry I'd like to preview the new interface for the Search, View New Content and User Content views, since all three of these views essentially share the same interface elements. We previously covered the changes and improvements to View New Content, and these changes have been incorporated into the new interface. As always, please note that screenshots are from development software, and are subject to change before the final release. Overview The biggest change is of course a brand new filter menu, to replace the myriad dropdown menus that we currently use. Content can be effortlessly filtered with single clicks - whether it's changing the app you're searching, the sub-section, or the time frame. Search sorting In addition to the main filter menu, search results also make use of a new filter bar to enable sorting and ordering (again replacing dropdown menus). This is an interface concept that you can expect to see more of in IP.Board 3.2. Language Non-English users will be pleased to know that we're now making use of IPSLib::getAppTitle() for tabs and titles in the search interfaces, so that the correct translation is always displayed. We hope you like this brief preview of the new search interface! The changes we've shown here and elsewhere are a taste of the improvements we're making across the product to improve usability and aesthetics. There's plenty more to come, in our blog entries and public previews. Feel free to comment on this blog entry below or, if you have feedback unrelated to this blog entry, start a new topic in our feedback forum. Be sure to check the What's New in IP.Board 3.2 topic for a running list of announced changes!
  24. While we aren't quite ready to show off IP.Board 3.2.0 in its entirety yet, I wanted to talk a little about what you can expect from our new front-end skin - our thinking behind development, areas we've focused on, etc. Goals Before development commenced, we created a list of goals that we aimed to meet. These include: Improving the experience We've been accused in the past of having a 'clunky' experience in some areas, so we've worked hard to improve the user experience in key areas. Some of these are usability changes, while others relate to reducing clutter on the screen or relocating elements. Making the skin easier for 3rd party developers (and ourselves!) to use Another focus was on making our skin - particularly the CSS - easier to use, for skinners, developers, even our own team. We've found that as new features were added, whole blocks of existing CSS were copied, pasted, then tweaked to suit the new feature. This creates some resource overhead, not to mention more work for skinners and developers. To remedy this, we've begun introducing a new approach to our CSS: more modular units that can be reused and combined easily to make many interfaces easy to implement. This will be gradually introduced throughout the skin as areas get updated in future point releases. Already, we've been able to remove large sections of old CSS as the updated areas make use of the new modular CSS. Modernizing the display It's been 3 years since we first began working on IP.Board 3.0, and the internet has come some way since then. We decided it was time to refresh the visual style of our default skin. While it will still be clearly recognizable as IP.Board, we've added some polish to the overall style, and made more use of technologies such as CSS3. Areas that we've focused on A huge number of areas have had minor tweaks here and there, too numerous to list here. But other areas have had significant updates, including: Global wrapper This is probably a given, since it influences the rest of the skin. Parts have been made more streamlined, with a new member & moderator bar, a new application bar that now supports as many apps as you choose to install, inline sign in and more. Forum view While the basic concept of listing topics inside a forum hasn't been altered, we've made big updates to the presentation, including rethinking the display of non-essential items and a new, friendly topic preview. Topics view As with forum view, the basic structure has not changed (familiarity is a good thing!), but numerous elements have been tweaked, changed, and tidied. All in all, reading topics should be a more pleasant experience for your users. Profile Our profile view was in need of overhauling, so that's what we've done! It now sports a vastly cleaner display while keeping all the relevant information accessible. Data from 3rd party apps is easily incorporated without fear of breaking the tab bar as in 3.0. In addition, we have merged profile comments and status updates, giving members a modern 'wall' in their profile. Calendar We've previously posted blog entries about new features you can expect to see in Calendar, and to go along with that, we've completely revamped almost every aspect of the calendar display - hopefully making it a more inviting place for your community to share events. Registration We identified some time ago that our registration routine was less than optimal, especially when considering communities that make use of IP.Nexus, with its ability to sell packages at registration. To remedy that, we have rethought the registration routine, to present Nexus packages in a better way, get rid of unnecessary steps along the way, and above all make it quicker for your prospective members to register Moderation We will elaborate on this point at a later date, but we have made the various day-to-day moderation tasks easy to access and use - a boon for your moderating team. View New Content Our View New Content area has steadily grown more complex as additional sorting and filtering options have been introduced, so we felt it was time to improve the display of those options to make it all a bit easier to use. Posting Screen We've updated the new topic/reply screen to be more streamlined, as well as integrate some new goodies that we'll talk about later. Redirect Screens The old-fashioned 'you are being redirected' screens are now a thing of the past. Every area has been updated to use a new inline notification display, which doesn't get in the way and fades after a few seconds. Teasers To whet your appetite, here's some teaser shots of the new front-end. We'll be gradually showing more as we discuss new features in later blog entries. While these are just teaser shots, over the next few weeks we will start posting blog entries about new features on the front end that will also contain screenshots of new skin elements so keep an eye on our blog for more updates! Feel free to comment on this blog entry below or, if you have feedback unrelated to this blog entry, start a new topic in our feedback forum. Be sure to check the What's New in IP.Board 3.2 topic for a running list of announced changes!
  25. One of the areas we identified as requiring significant update in IP.Board 3.2.0 was the member management process. Member management is, for most communities, the most frequently-executed task that happens in the AdminCP, so it is important the process is quick and easy. In previous 3.x releases, each different type of member (normal, banned, validating etc.) had a separate page from which you could manage that type. Once on the correct page, you check the members you want to work with, then select an action. Refresh, repeat. In IP.Board 3.2.0, we've significantly streamlined the entire process. All types of members are now viewed in a single interface, though that interface can adapt depending on what you're viewing. Let's take a look... Viewing Members Notice at top-left a toggle bar, to change the member type you're viewing. Notice also that the column headers (member name and email, in this case) allow for sorting. All of this takes place dynamically - virtually no page loads are required for any action in the new Member Management section. Searching for Members Searching for members is easy - simply start typing in the filter box, and results are displayed live in the list. For more specific searching, you can use advanced search to narrow your criteria. Managing Members Managing members is a one-click process. The available options for each member are presented as buttons; clicking an action confirms it and removes that member from the list. Previously, you would have to (in this example) select the members to ban, scroll down, choose the appropriate action, the page would reload, then you'd have to do the same for the members you wanted to delete. In IP.Board 3.2, you can simply work your way down the list, choosing the appropriate option for each user. We expect this will be particularly useful when managing validating members. Once you've cleared the list, the page will automatically load in another 20 members for you to manage. You can still mass-manage members, however. Selecting some members enables the available mass-actions, and again, once you confirm, more members are loaded for you automatically. Summary Member Management is now essentially a one-click process - a vast improvement on previous version of IP.Board. We hope that the new process will significantly reduce the time you spend on the repetitive and time-consuming task of managing your member base. Feel free to comment on this blog entry below or, if you have feedback unrelated to this blog entry, start a new topic in our feedback forum. Be sure to check the What's New in IP.Board 3.2 topic for a running list of announced changes!