I was trying to expand the extension which offers to see linked accounts for each user on the forum. It works in memberlist_view page and I would like to have it inside viewtopic_body.
I have a difficulty with narrowing the linked accounts to particular post id and author.
The closest I got is to have linked accounts from each user within the topic.
/** * Assign functions defined in this class to event listeners in the core * * #return array */ static public function getSubscribedEvents(): array { return array( 'core.viewtopic_modify_post_row' => 'viewtopic_linked_accounts_list',
` /**
* Show list of linked accounts in every post.
*
* #param data $event The event object
*
* #return void
*/
public function viewtopic_linked_accounts_list(data $event): void
{
// $this->template->assign_var('U_CAN_VIEW_LINKED_ACCOUNTS', $this->auth->acl_get('u_view_other_users_linked_accounts'));
foreach ($this->linking_service->get_linked_accounts($event['row']['user_id']) as $account)
{
$this->template->assign_block_vars('linked_accounts', array(
'ID' => $account['user_id'],
'USERNAME' => get_username_string('full', $account['user_id'], $account['username'], $account['user_colour']),
));
}
}`
Before creating an own solution I tried finding something which already suits my needs. I have got a node.js server where multiple clients / applications connect to. These clients will send log messages to the server which I would like to display in a panel.
Now there are some feature I that I need for a typical multiline textbox for logmessages:
I need to be able to append log messages as they will be send regularly via websockets
It should autoscrolldown unless the user is selecting text or scrolling up
It should be able to use colors and bold/regular
My question:
Is there already a solution for the above use case?
Can I give you my example? It used to be a textarea but I've refactored it to a div with little code changes.
Some highlights of the code, available on github
A custom function to send log messages:
/**
* Add a message to the gamelog
* #param {Object} options : allows custom output
* #param {String} options.message : the message to display
* #param {Boolean} options.isTimed : does the message has a timestamp in front of it?
* #param {Boolean} options.isError : is the message an error?
* #param {Boolean} options.isNewline : start the message on a new line
*/
addMessage: function (options) {
var instance = ns.instance,
audio = instance.audio,
audiofx = audio.settings.fx,
history = this.areaMessage.html();
// isTimed?
options.message = options.isTimed
? history + this.fieldClock.val() + ': ' + options.message
: history + options.message;
// isNewline?
if (options.isNewline) {
options.message = options.message + '<br />';
}
// message
this.areaMessage.html(options.message);
this.scrollTop(this.areaMessage);
// isError?
if (options.isError) {
audio.play(audiofx.error);
}
},
A scroll to top function:
/**
* Automatically scroll down (from the top)
* #param {Object} target : jQuery object
*/
scrollTop: function (target) {
target.scrollTop(99999);
target.scrollTop(target.scrollTop() * 12);
}
To use colored messages you should be able to use an HTML string:
log.addMessage({
message: '<span style="color: red;">[ERROR]</span> ',
isNewLine: false
});
log.addMessage({
message: 'the rest of the error message',
isNewLine: true
});
Feel free to use this idea to enroll your own custom message box.
I'm using protractor for automation testing, at this time all tests work properly.
But if I set test running parallel
add to config file:
shardTestFiles: true,
maxInstances: 4,
tests will start randomly fail throwing 'no element found' and/or 'failed expectation' exceptions.
Could someone suggest what can be cause of this problem or how to fix it?
Could be a cause that test fails at the moment when start running new browser instance and Protractor focusing on it?
(At this moment i have about 20 spec files and each spec file running starts new browser instance)
environment:
Windows 7 x64
Protractor v2.1.0
Browser Chrome v46
to solve my issue I've created a wrapper of elementFinder and ElementArrayFinder objects with overridden methods and some additional waiting for visibility and/or for presence.
Example for elementFinder.getText() and elementFinder.click()
function ElementFinderWrapper() {
var conditions = protractor.ExpectedConditions;
/**
* Returns a wrapper for ElementFinder element.
* #param {webdriver.Locator} locator
*/
this.get = function(locator){
return new Control(element(locator));
};
/**
* Creates a wrapper for ElementFinder element.
* #param {ElementFinder} element_finder
* #constructor
*/
function Control(element_finder) {
/**
* Returns the visible innerText of this element.
* #returns {!webdriver.promise.Promise.<string>}
*/
this.getText = function () {
return browser.wait(conditions.presenceOf(element_finder), 3000);
.then(function () {
return browser.wait(conditions.visibilityOf(element_finder), 3000);
})
.then(function () {
return element_finder.getText();
});
};
/**
* Clicks on visible element.
* #returns {!webdriver.promise.Promise.<void>}
*/
this.click = function () {
return browser.wait(conditions.presenceOf(element_finder), 3000);
.then(function () {
return browser.wait(conditions.visibilityOf(element_finder), 3000);
})
.then(function () {
return element_finder.click();
});
};
};
};
then use something like:
contol = ElementFinderWrapper.get(by.xpath('some path'));
control.getText();
control.click();
We have a collection of persons, and a collection of adresses. In each person, there's an id for the adress. We try do do a 'join-like' in JavaScript, and it's not possible to add new field in the return object.
var ret;
app.get('/tp/show/method', function (req, res) {
ret={};
User2Model.find(function(err, users) {
if(err){
console.error('Error find: '+err);
}
var last_id;
for(var user_id in users){
last_id=users[user_id]._id;
ret[last_id]=users[user_id];
}
for(var user_id in users){
AdrModel.find({ 'user_id': users[user_id]['id_adr'] },function(err, adr) {
if (err){
console.error('Error find: '+err);
}
for(var i in adr){
for(var user_id in users){
if (users[user_id].id_adr==adr[i].user_id) {
/* ok id found, so add all adresses to the result: */
/* The following line doesn't work: */
ret[users[user_id]._id]=adr;
if(users[user_id]._id==last_id){
var url_parts = url.parse(req.url, true);
var query = url_parts.query;
res.setHeader(
'content-type', 'application/javascript'
);
json=query.callback+'('+JSON.stringify({
data:{success: 1, value: ret }
})+')';
res.send(json);
}
break;
}
}
}
});
}
});
});
The variable ret is global, so we should be able to modify it, but the return result just accept when we override some of the properties already there. It doesn't work if we try to add new properties like "addr". What am I missing?
This is a typical problem caused by trying to handle asynchronous code with synchronous means. Your entire attempt is unfixable, you need to scrap it.
One widely adopted way of handling asynchronous code without going insane is by using promises.
Here is what your code could look like if you used a promise library. For the sake of the example I'm using Bluebird here.
var findAddress = Promise.promisify(AdrModel.find);
var findUsers = Promise.promisify(User2Model.find);
// a helper function that accepts a user object and resolves the address ID
function attachAddrToUser(user) {
return findAddress({
user_id: user.id_adr
}).then(function (address) {
user.address = address;
return user;
}).catch(function (e) {
console.error("error finding address for user ID " + user.id_user, e);
});
}
findUsers().then(function (users) {
var pending = [], id_user;
for (id_user in users) {
pending.push(attachAddrToUser(users[id_user]));
}
Promise.all(pending).then(function (users) {
// all done, build and send response JSON here
}).catch(function (e) {
// don't forget to add error handling
});
});
working jsFiddle over here: http://jsfiddle.net/Tomalak/2hdru6ma/
Note: attachAddrToUser() modifies the user object that you pass to it. This is not entirely clean, but it's effective in this context.
As I indicated in comments to #Tomalak's solution above, events can also be used to manage program flow in an async environment.
Here's an alternate partial solution that uses just that approach.
Please note that of the various ways of accomplishing this goal (I know of at least three, or four if you accept that the pain of "Callback Hell" can be ameliorated through the use of callbacks defined outside of and only referenced inline by their caller), I prefer using events since they are a more natural way for me to think about this class of problem.
Take-aways
Events are an efficient and easily understandable way to manage program flow in an async programming environment.
Rather than simple triggers, events can be used transport any data so they can be used further on for any purpose.
Events can easily call other events without worrying about scope.
Event processing allows you to unwind your code such that it becomes easier to track, and thus debug, as well as reducing the burden on the stack typically seen in deeply nested or recursive code. In other words, events are fast and very memory efficient.
Explanation
The code first defines two mocks:
an App class which provides a get method, allowing us to mock out the OP's app instance, and
a User2Model singleton that provides a find function for the same purpose.
It then documents the following events:
error - which is called on any errors to print a message to console and exit the program
get - which is fired with the result of the app.get method and immediately fires the processUsers event with {req:req,res:res}
processUsers - fired by the get event handler with a mocked array of user objects, sets up a results object and a last_id value, and then calls the nextUser event.
nextUser - fired by the processUsers event which picks the next user off the users array, sets evt.last_id, adds the user to the evt.results, and emits itself, or if there are no users left on the evt.users array, emits complete
complete - fired by nextUser and simply prints a message to console.
Event handlers are next defined using the 'on'+eventName convention.
And finally, we
define an eventHandlers object, to map handlers to their appropriate events,
instantiate our app instance, and
invoke its get method with a callback that simply emits a get event to start the ball rolling.
I've documented most of the solution using jsdoc and added logging messages to show progress as each event is emitted and its handler invoked. The result of the run is included after the code. (The http req and res objects have been commented out of the log messages for the sake of brevity.)
One final note, while this example is 269 lines long, most of it is documentation.
The actual code (without the mocks) is only about 20 or 25 lines.
Code
/*
Example of using events to orchestrate program flow in an async
environment.
*/
var util = require('util'),
EventEmitter = require('events').EventEmitter;
// mocks
/**
* Class for app object (MOCK)
* #constructor
* #augments EventEmitter
*/
var App = function (handlers) {
EventEmitter.call(this);
this.init(handlers);
};
util.inherits(App, EventEmitter);
/**
* Inits instance by setting event handlers
*
* #param {object} handlers
* #returns {App}
*/
App.prototype.init = function (handlers) {
var self = this;
// set event handlers
Object.keys(handlers).forEach(function (name) {
self.on(name, handlers[name]);
});
return self;
};
/**
* Invokes callback with req and res
* #param uri
* #param {App~getCallback} cb
*/
App.prototype.get = function (uri, cb) {
console.log('in app.get');
var req = {uri: uri},
res = {uri: uri};
/**
* #callback App~getCallback
* #param {object} req - http request
* #param {object} res - http response
* #fires {App#event:get}
*/
cb(req, res);
};
/**
* Data access adapter - (MOCK)
* #type {object}
*/
var User2Model = {};
/**
*
* #param {User2Model~findCallback} cb
*/
User2Model.find = function (cb) {
var err = null,
users = [
{_id: 1},
{_id: 2}
];
/**
* #callback User2Model~findCallback
* #param {Error} err
* #param {Array} users
*/
cb(err, users);
};
// events
/**
* Error event.
*
* #event App#error
* #type {object}
* #property {object} [req] - http request
* #property {object} [res] - http response
* #property {string} where - name of the function in which the error occurred
* #property {Error} err - the error object
*/
/**
* Get event - called with the result of app.get
*
* #event App#get
* #type {object}
* #property {object} req - http request
* #property {object} res - http response
*/
/**
* ProcessUsers event - called
*
* #event App#processUsers
* #type {object}
* #property {object} req - http request
* #property {object} res - http response
* #property {Array} users - users
*/
/**
* NextUser event.
*
* #event App#nextUser
* #type {object}
* #property {object} req - http request
* #property {object} res - http response
* #property {Array} users
* #property {*} last_id
* #property {object} result
*/
/**
* Complete event.
*
* #event App#complete
* #type {object}
* #property {object} req - http request
* #property {object} res - http response
* #property {Array} users
* #property {*} last_id
* #property {object} result
*/
// event handlers
/**
* Generic error handler
*
* #param {App#event:error} evt
*
* #listens App#error
*/
var onError = function (evt) {
console.error('program error in %s: %s', evt.where, evt.err);
process.exit(-1);
};
/**
* Event handler called with result of app.get
*
* #param {App#event:get} evt - the event object
*
* #listens App#appGet
* #fires App#error
* #fires App#processUsers
*/
var onGet = function (evt) {
console.log('in onGet');
var self = this;
User2Model.find(function (err, users) {
if (err) {
console.log('\tonGet emits an error');
return self.emit('error', {
res:evt.res,
req:evt.req,
where: 'User2Model.find',
err: err
});
}
self.emit('processUsers', {
//req:req,
//res:res,
users: users
});
});
};
/**
* Handler called to process users array returned from User2Model.find
*
* #param {App#event:processUsers} evt - event object
* #property {object} req - http request
* #property {object} res - http response
* #property {Array} users - array of Users
*
* #listens {App#event:processUsers}
* #fires {App#event:nextUser}
*/
var onProcessUsers = function (evt) {
console.log('in onProcessUsers: %s', util.inspect(evt));
var self = this;
evt.last_id = null;
evt.result = {};
self.emit('nextUser', evt);
};
/**
* Handler called to process a single user
*
* #param evt
* #property {Array} users
* #property {*} last_id
* #property {object} result
*
* #listens {App#event:nextUser}
* #emits {App#event:nextUser}
* #emits {App#event:complete}
*/
var onNextUser = function (evt) {
var self = this;
console.log('in onNextUser: %s', util.inspect(evt));
if (!(Array.isArray(evt.users) && evt.users.length > 0)) {
return self.emit('complete', evt);
}
var user = evt.users.shift();
evt.last_id = user._id;
evt.result[evt.last_id] = user;
self.emit('nextUser', evt);
};
/**
* Handler invoked when processing is complete.
*
* #param evt
* #property {Array} users
* #property {*} last_id
* #property {object} result
*/
var onComplete = function (evt) {
console.log('in onComplete: %s', util.inspect(evt));
};
// main entry point
var eventHandlers = { // map our handlers to events
error: onError,
get: onGet,
processUsers: onProcessUsers,
nextUser: onNextUser,
complete: onComplete
};
var app = new App(eventHandlers); // create our test runner.
app.get('/tp/show/method', function (req, res) { // and invoke it.
app.emit('get', {
req: req,
res: res
});
/* note:
For this example, req and res are added to the evt
but are ignored.
In a working application, they would be used to
return a result or an error, should the need arise,
via res.send().
*/
});
Result
in app.get
in onGet
in onProcessUsers: { users: [ { _id: 1 }, { _id: 2 } ] }
in onNextUser: { users: [ { _id: 1 }, { _id: 2 } ], last_id: null, result: {} }
in onNextUser: { users: [ { _id: 2 } ],
last_id: 1,
result: { '1': { _id: 1 } } }
in onNextUser: { users: [],
last_id: 2,
result: { '1': { _id: 1 }, '2': { _id: 2 } } }
in onComplete: { users: [],
last_id: 2,
result: { '1': { _id: 1 }, '2': { _id: 2 } } }
Well, I get it. If the function AdrModel.find is async, you're setting this values always to the last user.
This occurs because a async function will be executed after the for block end. So, the value of the user_id in all AdrModel.find calls will be always the same, because the saved scope where the async call is executed. Let's say your users are this collection
[{_id: 0}, {_id:2}, {_id: 3}]
So the calls of AdrModel.find will always use user_id -> 3 value:
ret[users[user_id]._id]=adr; //this guy will use user_id == 3, three times
EDIT
To resolve your problem is simple, modularize your code.
Create a function to do this resource gathering:
function setAdr(userId){
AdrModel.find({ 'user_id': userId },function(err, adr) {
...
}
}
And then, you call it in your 'for':
...
for(var user_id in users){
setAdr(users[user_id].id_adr);
...
This way you'll save a safe scope for each async call.
Using a technique borrowed from http://www.gutterbling.com/blog/synchronous-javascript-evaluation-in-android-webview/ we have successfully implemented a number of features within our app that allow our Android app to synchronously get data from a Webview.
Here's the example from gutterbling:
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import android.content.Context;
import android.util.Log;
import android.webkit.WebView;
/**
* Provides an interface for getting synchronous javascript calls
* #author btate
*
*/
public class SynchronousJavascriptInterface {
/** The TAG for logging. */
private static final String TAG = "SynchronousJavascriptInterface";
/** The javascript interface name for adding to web view. */
private final String interfaceName = "SynchJS";
/** Countdown latch used to wait for result. */
private CountDownLatch latch = null;
/** Return value to wait for. */
private String returnValue;
/**
* Base Constructor.
*/
public SynchronousJavascriptInterface() {
}
/**
* Evaluates the expression and returns the value.
* #param webView
* #param expression
* #return
*/
public String getJSValue(WebView webView, String expression)
{
latch = new CountDownLatch(1);
String code = "javascript:window." + interfaceName + ".setValue((function(){try{return " + expression
+ "+\"\";}catch(js_eval_err){return '';}})());";
webView.loadUrl(code);
try {
// Set a 1 second timeout in case there's an error
latch.await(1, TimeUnit.SECONDS);
return returnValue;
} catch (InterruptedException e) {
Log.e(TAG, "Interrupted", e);
}
return null;
}
/**
* Receives the value from the javascript.
* #param value
*/
public void setValue(String value)
{
returnValue = value;
try { latch.countDown(); } catch (Exception e) {}
}
/**
* Gets the interface name
* #return
*/
public String getInterfaceName(){
return this.interfaceName;
}
}
This JS Interface is used like this:
WebView webView = new WebView(context);
SynchronousJavascriptInterface jsInterface = new jsInterface();
webView.addJavascriptInterface(jsInterface, jsInterface.getInterfaceName());
String jsResult = jsInterface.getJSValue(webView, "2 + 5");
Despite working nicely in Android 4.0 - 4.4.4 this is not working for us with Android 5.0 (Lollipop).
It appears as though in Lollipop the JS executes after the latch countdown has completed, whereas previously it would return a value prior to the countdown completing.
Has something changed with the threads that JS in an Webview executes on? And is there any way that I can fix this without re-writing the large chunks of our app that depend on being able to call the JS synchronously?
loadUrl("javascript:" + code)
don't work with API > 19, instead use:
evaluateJavascript(code, null);
Or, you can improve your code to use the callback provided by evaluateJavascript, though.