add an image property to a domain class in Grails - mysql

I have a domain class in Grails and I want to add a picture attribute (the image of the user) A detailed answer will be helpful for a Grails beginner like me.
What will be the type of the attribute? How can I store it in a database (MySQL)?

I have implemented functionality like this in this way:
First of all domain class. I add 3 properties to the class where I can store byte array with image and image properties like image type (gif, png, jpg) and image original name.
class User {
byte[] image;
String imageName;
String imageContentType;
static constraints = {
image nullable: true, blank: true, maxSize: 1024 * 1024 * 20; //20MB
imageName nullable: true, blank: true;
imageContentType nullable: true, blank: true;
}
}
Secondly, you need to remember to have proper type of form to upload image to your server:
<g:uploadForm class="user-form">
Image: <g:field name="userImage" type="file" accept="image/*"/>
</g:uploadForm>
Thirdly, controller. 'create' action to get image from request and save it into database and 'getImage' action to get link to the image.
class UserController {
def create () {
def imageMap = [:];
def imageContentType = parameters.componentImageImageForm?.getContentType();
if (imageContentType == null || !(imageContentType =~ "image/")) {
imageMap.put("image", null);
imageMap.put("imageName", null);
imageMap.put("imageContentType", null);
} else {
imageMap.put("image", parameters.componentImageImageForm?.getBytes());
imageMap.put("imageName", parameters.componentImageImageForm?.getOriginalFilename());
imageMap.put("imageContentType", parameters.componentImageImageForm?.getContentType());
}
def newUser = new User(imageMap);
def validation = newUser.save(flush:true);
//if(validation == null) {
//validation failed
//} else {
//validation passed
//}
//you can return here some msg or something you need
}
def getImage(Long id) {
def user = User.get(id);
if (user != null) {
response.contentType = user.imageContentType == null ? "image/jpeg" : user.imageContentType;
response.contentLength = user.image == null ? 0 : user.image.size();
response.outputStream << user.image;
} else {
response.contentType = "image/jpeg";
response.contentLength = 0;
response.outputStream << null;
}
}
}
In the end, rendering an image in view (userId is of course id of user whose you want an image):
<img src="${createLink(action: 'getImage', controller: 'user', id: userId)}"/>

Related

CakePHP 4.x: How to access logged-in user info from within a custom Behavior?

I am trying to add a custom Behavior that is a clone of the default Timestamp. Mine is "Userstamp" adding the current user to audit-trail fields on all tables. At the point where Timestamp sets the field to "new FrozenTime($ts)", I want to set to "$identity->get('username')" or similar. I am having trouble reaching anything from Authentication/Authorization or Identity from within the Behavior.
I know that the information is there, somewhere. But what do I need to include in my Behavior class in order to retrieve it?
I found this link, but I don't see where to put the suggested code.
In my Table:
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('users');
$this->setDisplayField('name');
$this->setPrimaryKey('id');
$this->addBehavior('Timestamp');
$this->addBehavior('Userstamp');
.
.
.
}
Cake's timestamp Behavior:
public function timestamp(?DateTimeInterface $ts = null, bool $refreshTimestamp = false): DateTimeInterface
{
if ($ts) {
if ($this->_config['refreshTimestamp']) {
$this->_config['refreshTimestamp'] = false;
}
$this->_ts = new FrozenTime($ts);
} elseif ($this->_ts === null || $refreshTimestamp) {
$this->_ts = new FrozenTime();
}
return $this->_ts;
}
My userstamp Behavior:
public function userstamp($userstamp = null, bool $refreshUserstamp = false)
{
// Variations of this do not work, the Property is not available in UserstampBehavior
// $currentUser = $this->Authentication
// ->getIdentity()
// ->getIdentifier();
$currentUser = 'abc'; <<<<<<<<<<<<< Hard-coded temporarily
if ($userstamp) {
if ($this->_config['refreshUserstamp']) {
$this->_config['refreshUserstamp'] = false;
}
$this->_userstamp = $currentUser;
} elseif ($this->_userstamp === null || $refreshUserstamp) {
$this->_userstamp = $currentUser;
}
return $this->_userstamp;
}
Your Auth-Informations lives also in the session.
So you can access Session-stuff in a table-class like this:
(new Session)->read('Auth')
And therefore you give this information from a table-class to your Behaviour like this:
$this->addBehavior('Userstamp', ['user_info'=>(new Session)->read('Auth')]);
Then you can access this information in your behaviour:
public function initialize(array $config){
$this->user_info = $config['user_info'];
}
public function myFunction(){
// do something with $this->user_info
}

How do I insert a value in a custom field in a table in Prestashop?

I added a custom field named "deldate" in "ps_orders" table, and I added a text box on "OPC" checkout page. Now when I click on order confirm button the value in the textbox should be saved in "deldate" field of "ps_orders" table.
The textbox is showing perfectly but in which files do I need to make changes to save the textbox value in the table?
(Theme is default one.)
class/order/order.php
class OrderCore extends ObjectModel
{
public $deldate;
}
And
public static $definition = array(
'fields' => array(
'deldate'=> array('type' => self::TYPE_STRING),
),
)
Shopping-cart.tpl
<div class="box">
<div class="required form-group">
<form method="post">
<label for="Fecha de entrega deseada">{l s='Desired delivery date' mod='deldate'}</label>
<input type="text" id="deldate" name="deldate" class="form-control" value="hello" />
</form>
</div>
</div>
Ok, I figured out the solution...
If you want to add some information to the order in the checkout process you have to save this informations elsewhere, if you look the cart table are very similar to order table.
Why you have to do this? Because you don't have an order before the confirmation by customer, so until the checkout is not complete that informations can't be saved in the order table.
So, first, create the field in database, in this case you have to add in ps_orders and in the ps_cart as well.
(In your case I suggest to use a DATETIME field)
Second, override the Order class:
class Order extends OrderCore
{
public function __construct($id = null, $id_lang = null)
{
self::$definition['fields']['deldate'] = array('type' => self::TYPE_DATE);
parent::__construct($id, $id_lang);
}
}
and the Cart class:
class Cart extends CartCore
{
public function __construct($id = null, $id_lang = null)
{
self::$definition['fields']['deldate'] = array('type' => self::TYPE_DATE);
parent::__construct($id, $id_lang);
}
}
Now we have to save the field during the checkout process, so we override the OrderController:
class OrderController extends OrderControllerCore
{
public function processAddress()
{
parent::processAddress();
// Here we begin our story
if(Tools::getIsset('deldate')) // Check if the field isn't empty
{
$deldate = Tools::getValue('deldate');
// Here you must parse and check data validity (I leave to you the code)
/* ... */
// Assign the data to context cart
$this->context->cart->deldate = $deldate;
// Save information
$this->context->cart->update();
}
}
}
Now you have to 'transport' this informations from the cart to the order, this will be done through the PaymentModule class, specifically with the validateOrder method.
So, another override:
class PaymentModule extends PaymentModuleCore
{
public function validateOrder($id_cart, $id_order_state, $amount_paid, $payment_method = 'Unknown', $message = null, $extra_vars = array(), $currency_special = null, $dont_touch_amount = false, $secure_key = false, Shop $shop = null)
{
$result = parent::validateOrder($id_cart, $id_order_state, $amount_paid, $payment_method, $message, $extra_vars, $currency_special, $dont_touch_amount, $secure_key, $shop);
if($result)
{
$oldcart = new Cart($id_cart);
$neworder = new Order($this->currentOrder);
$neworder->deldate = $oldcart->deldate;
$neworder->update();
return true; // important
}
else
{
return $result;
}
}
}
After all of this you have the deldate field saved. However, I absolutely don't suggest this method, it's more safe and simple with a module and hooks... But this is another story :)
This will works only with the five steps checkout.
For next code lines, God save me...
If you want to works with OPC you have to dirty your hands with JS and override the OrderOpcController.
Start with the JS, edit the order-opc.js in js folder of enabled theme, find bindInputs function and append this lines of code:
function bindInputs()
{
/* ... */
$('#deldate').on('change', function(e){
updateDelDateInput(); // custom function to update deldate
});
}
then append to the file your custom function:
function updateDelDateInput()
{
$.ajax({
type: 'POST',
headers: { "cache-control": "no-cache" },
url: orderOpcUrl + '?rand=' + new Date().getTime(),
async: false,
cache: false,
dataType : "json",
data: 'ajax=true&method=updateDelDate&deldate=' + encodeURIComponent($('#deldate').val()) + '&token=' + static_token ,
success: function(jsonData)
{
if (jsonData.hasError)
{
var errors = '';
for(var error in jsonData.errors)
//IE6 bug fix
if(error !== 'indexOf')
errors += $('<div />').html(jsonData.errors[error]).text() + "\n";
alert(errors);
}
// Here you can add code to display the correct updating of field
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
if (textStatus !== 'abort')
alert("TECHNICAL ERROR: unable to save message \n\nDetails:\nError thrown: " + XMLHttpRequest + "\n" + 'Text status: ' + textStatus);
}
});
}
Then override the OrderOpcController, copy all the init method and change the line of code as below:
class OrderOpcController extends OrderOpcControllerCore
{
public function init()
{
// parent::init(); // comment or delete this line
FrontController::init(); // Very important!
// Then in this switch `switch (Tools::getValue('method'))` add your case
/* ... */
case 'updateDelDate':
if(Tools::isSubmit('deldate'))
{
$deldate = urldecode(Tools::getValue('deldate'));
// Here you must parse and check data validity (I leave to you the code)
/* ... */
// Assign the data to context cart
$this->context->cart->deldate = $deldate;
// Save information
$this->context->cart->update();
$this->ajaxDie(true);
}
break;
/* ... */
}
}
Obviously, is necessary the override of Order, Cart and PaymentModule as well.
PS: I hope that I didn't forget anything.
You can try also with this module
https://www.prestashop.com/forums/topic/589259-m%C3%B3dulo-selector-fecha-en-pedido/?p=2489523
Try this in the override of the class Order
class Order extends OrderCore
{
public function __construct($id = null, $id_lang = null)
{
parent::__construct($id, $id_lang);
self::$definition['fields']['deldate'] = array('type' => self::TYPE_STRING);
Cache::clean('objectmodel_def_Order');
}
}
The Cache::clean is need because getDefinition tries to retrieve from cache, and cache is set without the override on parent::__construct
I then tried to create a new empty Order and get the definition fields and it showed there, so it should save to mysql
$order = new Order();
var_dump(ObjectModel::getDefinition($order));exit;

Grails use SQLQuery result as transient value in domain class rendering

Let's say i have the following domain classes:
The first domain class Tag.groovy allows me to build a dynamic category structure with different layers (layer1, layer2 and layer3). Each activity from Activity.groovy belongs to a certain category and is connected to its category via ActivityTag.groovy. So far so good. :)
Tag.groovy
class Tag {
String name
String layer
Tag parent
static transients = ['activityCount', 'children']
int activityCount
static constraints = {
name nullable: false, unique: true
layer nullable: false, inList:['generic','layer1','layer2','layer3']
parent nullable: true
}
Set<Tag> getChildren() {
return Tag.findAllByParent(this) as Set
}
Set<Activity> getActivities() {
return ActivityTag.findAllByTag(this)*.activity
}
Set<Activity> getActivities(VirtualCity virtualCity) {
def activitiesWithTag = ActivityTag.findAllByTag(this)*.activity
return activitiesWithTag.findAll({it.virtualCity==virtualCity})
}
def getActivityCount(){
if (this.layer == "layer1") {
return this.children*.children*.activityCount.sum().sum()
} else if (this.layer == "layer2") {
return this.children*.activityCount.sum()
} else {
return this.getActivities().size()
}
}
def getActivityCount(VirtualCity virtualCity){
if (this.layer == "layer1") {
return this.children*.children*.activityCount.sum().sum()
} else if (this.layer == "layer2") {
return this.children*.activityCount.sum()
} else {
return this.getActivities(virtualCity).size()
}
}
}
Activity.groovy
class ActivityTag {console
Activity activity
Tag tag
static mapping = {
id composite: ['activity', 'tag']
version false
}
}
ActivityTag.groovy
class Activity {
VirtualCity virtualCity
String name
}
Now I would like to render my categories as JSON in a tree view. Can you tell me how I can achieve this?
I tried render(template: "tree", collection: Tag.findAllByLayer("layer1"), var: 'tag')
with this template _tree.gson:
model {
Tag tag
}
json {
id tag.id
name tag.name
activityCount tag?.activityCount ?: null
children g.render(template: 'tree', collection: tag?.children, var: 'tag') ?: null
}
But this method fails with java.lang.reflect.InvocationTargetException: null

Store.sync() lose extraParams

I have treeStore with extraParams
proxy: {
extraParams: {hierarchy_id: '5'},
api: {
update: jsonrpc.dim_hier.modifyData,
read: jsonrpc.dim_hier.getData,
create: jsonrpc.dim_hier.addData,
destroy: jsonrpc.dim_hier.deleteData
}
}
but store.sync() does not send to server this extraParams.
I tried to send param like this
component.getStore().getProxy().setExtraParam(
'hierarchy_id', hierarchy_id);
component.getStore().sync();
and this
component.getStore().sync({
params :{
hierarchy_id: hierarchy_id}
});
and
component.getStore().getProxy().setExtraParams({
hierarchy_id: hierarchy_id
});
component.getStore().sync();
but none of this works
What did I do wrong?
Thank for any help
In method doRequest (Ext.data.proxy.Direct)
if (action === 'read') {
params = request.getParams();
} else {
params = request.getJsonData();
}
I override this method like this
params = request.getParams();
if (action !== 'read') {
params.rows = request.getJsonData();
}}
Try it:
var store = component.getStore();
Ext.apply(store.proxy, {
extraParams: {
hierarchy_id: hierarchy_id
}
});
TO SEND THE extraParams ALWAYS NOT ONLY ON READ OPERATIONS (CREATE, DESTROY, UPDATE)
I extended
Ext.data.proxy.Direct
and override
doRequest
It worked like a charm.
Using ExtJs 4.1.1
The original code is:
if (operation.action == 'read') {
// We need to pass params
method = fn.directCfg.method;
args = method.getArgs(params, me.paramOrder, me.paramsAsHash);
} else {
args.push(request.jsonData);
}
I changed it to:
method = fn.directCfg.method;
args = method.getArgs(params, me.paramOrder, me.paramsAsHash);
if(operation.action !== 'read'){
args.push(request.jsonData);
}
Took the idea from here https://www.sencha.com/forum/showthread.php?282879-ExtraParams-Store-Create
Notice: your store will have a proxy of whatever you put on the alias of the class you created. Your alias will be like alias : 'proxy.mycompanydirect' then your store will have a proxy type 'mycompanydirect'

grails exception: Tag [paginate] is missing required attribute [total]

I've the following method in my controller, that is used to list the Patient entities:
def list(Integer max) {
params.max = Math.min(max ?: 10, 100)
def roles = springSecurityService.getPrincipal().getAuthorities()
if(roles == 'ROLE_USER')
{
[patientInstanceList: Patient.findAllBySecUser(springSecurityService.getCurrentUser()), patientInstanceTotal: Patient.count()]
}
else if(roles == 'ROLE_ADMIN' || roles == 'ROLE_MANAGER')
{
[patientInstanceList: Patient.list(params), patientInstanceTotal: Patient.count()]
}
}
If I perform this method I have the exception
Tag [paginate] is missing required attribute [total]
But, if I use the following
def list(Integer max) {
params.max = Math.min(max ?: 10, 100)
[patientInstanceList: Patient.list(params), patientInstanceTotal: Patient.count()]
}
}
Everything works well. I see all the instances of Patient created. I only want that, based on role of user logged in, I can see a part of all the entities created. Why does this exception is raised?
I've found here Grails pagination tag error something similar, but no good answer was given
I'm not sure that you can ommit the return in a if statement. Another detail is that you're filtering your domain records in the ROLE_USER, but counting the total independent of this filter.
You can try this approach:
def list(Integer max) {
params.max = Math.min(max ?: 10, 100)
def roles = springSecurityService.getPrincipal().getAuthorities()
def model
//I think roles is a list so your equals will not work...
def admin = roles.find { it.authority == 'ROLE_ADMIN' || it.authority == 'ROLE_MANAGER' }
if(admin) {
model = [patientInstanceList: Patient.list(params), patientInstanceTotal: Patient.count()]
} else {
//createCriteria().list(params) will paginate...
def patientInstanceList = Patient.createCriteria().list(params) {
eq('secUser', springSecurityService.currentUser)
}
//totalCount will trigger a query without the limit, but applying your filter
model = [patientInstanceTotal: patientInstanceList.totalCount, patientInstanceList: patientInstanceList]
}
//and finally we return the model
//return model
//just to show that the return is optional
model
}