Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GroupView.js bug correction #2629

Closed
phili67 opened this issue Jun 28, 2017 · 10 comments
Closed

GroupView.js bug correction #2629

phili67 opened this issue Jun 28, 2017 · 10 comments
Assignees
Milestone

Comments

@phili67
Copy link

phili67 commented Jun 28, 2017

$(document).ready(function () {

  $.ajax({
    method: "GET",
    url: window.CRM.root + "/api/groups/" + window.CRM.currentGroup + "/roles",
    dataType: "json"
  }).done(function (data) {
    window.CRM.groupRoles = data.ListOptions;
    $("#newRoleSelection").select2({
      data: $(window.CRM.groupRoles).map(function () {
        return {
          id: this.OptionId,
          text: this.OptionName
        };
      })
    });
    initDataTable();
    //echo '<option value="' . $role['lst_OptionID'] . '">' . $role['lst_OptionName'] . '</option>';
  });

  $(".personSearch").select2({
    minimumInputLength: 2,
    ajax: {
      url: function (params) {
        return window.CRM.root + "/api/persons/search/" + params.term;
      },
      dataType: 'json',
      delay: 250,
      data: function (params) {
        return {
          q: params.term, // search term
          page: params.page
        };
      },
      processResults: function (rdata, page) {
        var idKey = 1;
        var results = new Array();
        data = JSON.parse(rdata);
        $.each(data[0].persons, function (index, cvalue) {
          var childObject = {
            id: idKey,
            objid: cvalue.id,
            text: cvalue.displayName,
            uri: cvalue.uri
          };
          idKey++;
          results.push(childObject);
        });
        return {results: results};
      },
      cache: true
    }
  });

  $("#targetGroupSelection").select2({
    ajax: {
      url: window.CRM.root + "/api/groups/",
      dataType: 'json',
      processResults: function (rdata, page) {
        var p = $.map(rdata.Groups, function (item) {
          var o = {
            text: item.Name,
            id: item.Id
          };
          return o;
        });
        return {results: p};
      }
    },
    minimumResultsForSearch: Infinity
  });

  $(".personSearch").on("select2:select", function (e) {
  	var dataT = $('#membersTable').DataTable();
  	
    $.ajax({
      method: "POST",
      url: window.CRM.root + "/api/groups/" + window.CRM.currentGroup + "/adduser/" + e.params.data.objid,
      dataType: "json"
    }).done(function (data) {
      var person = data.Person2group2roleP2g2rs[0];
      var node = dataT.row.add(person).node();
      dataT.rows().invalidate().draw(true);
      $(".personSearch").val(null).trigger('change')
    });
  });

  $("#deleteSelectedRows").click(function () {
  	var dataT = $('#membersTable').DataTable();
  	
    var deletedRows = dataT.rows('.selected').data()
    bootbox.confirm({
      message: "Are you sure you want to remove the " + deletedRows.length + " selected group members?",
      buttons: {
        confirm: {
          label: 'Yes',
            className: 'btn-success'
        },
        cancel: {
          label: 'No',
          className: 'btn-danger'
        }
      },
      callback: function (result)
      {
        if (result)
        {
          $.each(deletedRows, function (index, value) {
            $.ajax({
              type: 'POST', // define the type of HTTP verb we want to use (POST for our form)
              url: window.CRM.root + '/api/groups/' + window.CRM.currentGroup + '/removeuser/' + value.PersonId, // the url where we want to POST
              dataType: 'json', // what type of data do we expect back from the server
              data: {"_METHOD": "DELETE"},
              encode: true
            }).done(function (data) {
              dataT.row(function (idx, data, node) {
                if (data.PersonId == value.PersonId) {
                  return true;
                }
              }).remove();
              dataT.rows().invalidate().draw(true);
            });
          });
        }
       }
    });

  });

  $("#addSelectedToCart").click(function () {
    var dataT = $('#membersTable').DataTable();
  	
    if (dataT.rows('.selected').length > 0)
    {
      var selectedPersons = {
        "Persons" : $.map(dataT.rows('.selected').data(), function(val,i){
                      return val.PersonId;
                    })
      };
      $.ajax({
        type: 'POST',
        url: window.CRM.root + '/api/cart/',
        dataType: 'json',
        contentType: "application/json",
        data: JSON.stringify(selectedPersons)
      }).done(function(data) {
          if ( data.status == "success" )
          {
            location.reload();
          }
      });
    }

  });

  //copy membership
  $("#addSelectedToGroup").click(function () {
    $("#selectTargetGroupModal").modal("show");
    $("#targetGroupAction").val("copy");

  });

  $("#moveSelectedToGroup").click(function () {
    $("#selectTargetGroupModal").modal("show");
    $("#targetGroupAction").val("move");

  });


  $("#confirmTargetGroup").click(function () {
    var dataT = $('#membersTable').DataTable();

    var selectedRows = dataT.rows('.selected').data()
    var targetGroupId = $("#targetGroupSelection option:selected").val()
    var action = $("#targetGroupAction").val();

    $.each(selectedRows, function (index, value) {
      $.ajax({
        type: 'POST', // define the type of HTTP verb we want to use (POST for our form)
        url: window.CRM.root + '/api/groups/' + targetGroupId + '/adduser/' + value.PersonId,
        dataType: 'json', // what type of data do we expect back from the server
        encode: true
      });
      if (action == "move") {
        $.ajax({
          type: 'POST', // define the type of HTTP verb we want to use (POST for our form)
          url: window.CRM.root + '/api/groups/' + window.CRM.currentGroup + '/removeuser/' + value.PersonId,
          dataType: 'json', // what type of data do we expect back from the server
          encode: true,
          data: {"_METHOD": "DELETE"},
        }).done(function (data) {
          dataT.row(function (idx, data, node) {
            if (data.PersonId == value.PersonId) {
              return true;
            }
          }).remove();
          dataT.rows().invalidate().draw(true);
        });
      }
    });
    $(document).ajaxStop(function () {
      $("#selectTargetGroupModal").modal("hide");
    });
  });


  $(document).on("click", ".changeMembership", function (e) {
    var userid = $(e.currentTarget).data("personid");
    var dataT = $('#membersTable').DataTable();

    $("#changingMemberID").val(dataT.row(function (idx, data, node) {
      if (data.PersonId == userid) {
        return true;
      }
    }).data().PersonId);
    $("#changingMemberName").text(dataT.row(function (idx, data, node) {
      if (data.PersonId == userid) {
        return true;
      }
    }).data().firstName);
    $('#changeMembership').modal('show');
    e.stopPropagation();
  });

  $(document).on("click", "#confirmMembershipChange", function (e) {
    var changingMemberID = $("#changingMemberID").val();
    $.ajax({
      method: "POST",
      url: window.CRM.root + "/api/groups/" + window.CRM.currentGroup + "/userRole/" + changingMemberID,
      data: JSON.stringify({'roleID': $("#newRoleSelection option:selected").val()}),
      dataType: "json",
      contentType: "application/json; charset=utf-8",
    }).done(function (data) {
      dataT.row(function (idx, data, node) {
        if (data.PersonId == changingMemberID) {
          data.RoleId = $("#newRoleSelection option:selected").val();
          return true;
        }
      }).data();
      dataT.rows().invalidate().draw(true);
      $('#changeMembership').modal('hide');
    });
  });

});

function initDataTable() {
  dataT = $("#membersTable").dataTable({
    "language": {
      "url": window.CRM.root + "/skin/locale/datatables/" + window.CRM.locale + ".json"
    },
    "dom": 'T<"clear">lfrtip',
    "tableTools": {
      "sSwfPath": "//cdn.datatables.net/tabletools/2.2.3/swf/copy_csv_xls_pdf.swf",
      "sRowSelect": "multi",
      "aButtons": [
      {
        "sExtends": "csv",
        "bSelectedOnly": true
      }]
    },
    responsive: true,
    ajax: {
      url: window.CRM.root + "/api/groups/" + window.CRM.currentGroup + "/members",
      dataSrc: "Person2group2roleP2g2rs"
    },
    columns: [
      {
        width: 'auto',
        title: 'Name',
        data: 'PersonId',
        render: function (data, type, full, meta) {
          return '<img data-name="'+full.Person.FirstName + ' ' + full.Person.LastName + '" data-src="' + window.CRM.root + '/api/persons/' + full.PersonId + '/thumbnail" class="direct-chat-img initials-image"> &nbsp <a href="PersonView.php?PersonID="' + full.PersonId + '"><a target="_top" href="PersonView.php?PersonID=' + full.PersonId + '">' + full.Person.FirstName + " " + full.Person.LastName + '</a>';
        }
      },
      {
        width: 'auto',
        title: 'Group Role',
        data: 'RoleId',
        render: function (data, type, full, meta) {
          thisRole = $(window.CRM.groupRoles).filter(function (index, item) {
            return item.OptionId == data
          })[0];
          return thisRole.OptionName + '<button class="changeMembership" data-personid=' + full.PersonId + '><i class="fa fa-pencil"></i></button>';
        }
      },
      {
        width: 'auto',
        title: 'Address',
        render: function (data, type, full, meta) {
          return full.Person.Address1 + " " + full.Person.Address2;
        }
      },
      {
        width: 'auto',
        title: 'City',
        data: 'Person.City'
      },
      {
        width: 'auto',
        title: 'State',
        data: 'Person.State'
      },
      {
        width: 'auto',
        title: 'ZIP',
        data: 'Person.Zip'
      },
      {
        width: 'auto',
        title: 'Cell Phone',
        data: 'Person.CellPhone'
      },
      {
        width: 'auto',
        title: 'E-mail',
        data: 'Person.Email'
      }
    ],
    "fnDrawCallback": function (oSettings) {
      $("#iTotalMembers").text(oSettings.aoData.length);
      $("#membersTable .initials-image").initial();
    },
    "createdRow": function (row, data, index) {
      $(row).addClass("groupRow");
    }
  });

    $('#isGroupActive').change(function() {
        $.ajax({
            type: 'POST', // define the type of HTTP verb we want to use (POST for our form)
            url: window.CRM.root + '/api/groups/' + window.CRM.currentGroup + '/settings/active/' + $(this).prop('checked'),
            dataType: 'json', // what type of data do we expect back from the server
            encode: true
        });
    });

    $('#isGroupEmailExport').change(function() {
        $.ajax({
            type: 'POST', // define the type of HTTP verb we want to use (POST for our form)
            url: window.CRM.root + '/api/groups/' + window.CRM.currentGroup + '/settings/email/export/' + $(this).prop('checked'),
            dataType: 'json', // what type of data do we expect back from the server
            encode: true
        });
    });

  $(document).on('click', '.groupRow', function () {
    //$(this).toggleClass('selected');       
    var dataT = $('#membersTable').DataTable();
    var selectedRows = dataT.rows('.selected').data().length;
    
    $("#deleteSelectedRows").prop('disabled', !(selectedRows));
    $("#deleteSelectedRows").text("Remove (" + selectedRows + ") Members from group");
    $("#buttonDropdown").prop('disabled', !(selectedRows));
    $("#addSelectedToGroup").prop('disabled', !(selectedRows));
    $("#addSelectedToGroup").html("Add  (" + selectedRows + ") Members to another group");
    $("#addSelectedToCart").prop('disabled', !(selectedRows));
    $("#addSelectedToCart").html("Add  (" + selectedRows + ") Members to cart");
    $("#moveSelectedToGroup").prop('disabled', !(selectedRows));
    $("#moveSelectedToGroup").html("Move  (" + selectedRows + ") Members to another group");
  });

}

@DawoudIO
Copy link
Contributor

I'll create a PR and put you in it to review

@phili67
Copy link
Author

phili67 commented Jun 28, 2017

Thank's a lot.
In the other thread I told that there's a bug in the translation of the
Ok I add a correction to the translation process, bootstrap-datepicker.js

I have to add In the : Include/Footer.php

<script src="/skin/adminlte/plugins/datepicker/bootstrap-datepicker.js"></script>

<script src="/skin/fullcalendar/fullcalendar.js"></script>

<script src="/skin/adminlte/plugins/daterangepicker/daterangepicker.js"></script>

This solve the datePicker translation problem.

Second,
I have a question to ask you.
Is there a way when some people are in the cart ton include them directly in a group and how ?
When I do an extraction in the Query Menu Item of children in a range of ages, I would like to incorporate them directly in the group I've defined in the sundy school group defined by aged.
It should be a nice idea to incorporate them directly with a button in the cart view.

@phili67
Copy link
Author

phili67 commented Jun 28, 2017

Hi again, google API has change :
only https query to gogglemap are available.

So I update the code : /vendor/willdurand/geocoder/src/Geocoder/Provider/GoogleMaps.php
I've change :
const ENDPOINT_URL = 'http://maps.googleapis.com/maps/api/geocode/json?address=%s';
to :
const ENDPOINT_URL = 'https://maps.googleapis.com/maps/api/geocode/json?address=%s';

<?php

/**
 * This file is part of the Geocoder package.
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @license    MIT License
 */

namespace Geocoder\Provider;

use Exception;
use Geocoder\Exception\InvalidCredentials;
use Geocoder\Exception\NoResult;
use Geocoder\Exception\QuotaExceeded;
use Geocoder\Exception\UnsupportedOperation;
use Ivory\HttpAdapter\HttpAdapterInterface;

/**
 * @author William Durand <william.durand1@gmail.com>
 */
class GoogleMaps extends AbstractHttpProvider implements LocaleAwareProvider
{
    /**
     * @var string
     */
    const ENDPOINT_URL = 'https://maps.googleapis.com/maps/api/geocode/json?address=%s';

    /**
     * @var string
     */
    const ENDPOINT_URL_SSL = 'https://maps.googleapis.com/maps/api/geocode/json?address=%s';

    use LocaleTrait;

    /**
     * @var string
     */
    private $region;

    /**
     * @var bool
     */
    private $useSsl;

    /**
     * @var string
     */
    private $apiKey;

    /**
     * @param HttpAdapterInterface $adapter An HTTP adapter
     * @param string               $locale  A locale (optional)
     * @param string               $region  Region biasing (optional)
     * @param bool                 $useSsl  Whether to use an SSL connection (optional)
     * @param string               $apiKey  Google Geocoding API key (optional)
     */
    public function __construct(HttpAdapterInterface $adapter, $locale = null, $region = null, $useSsl = false, $apiKey = null)
    {
        parent::__construct($adapter);

        $this->locale = $locale;
        $this->region = $region;
        $this->useSsl = $useSsl;
        $this->apiKey = $apiKey;
    }

    /**
     * {@inheritDoc}
     */
    public function geocode($address)
    {
        // Google API returns invalid data if IP address given
        // This API doesn't handle IPs
        if (filter_var($address, FILTER_VALIDATE_IP)) {
            throw new UnsupportedOperation('The GoogleMaps provider does not support IP addresses, only street addresses.');
        }

        $query = sprintf(
            $this->useSsl ? self::ENDPOINT_URL_SSL : self::ENDPOINT_URL,
            rawurlencode($address)
        );

        return $this->executeQuery($query);
    }

    /**
     * {@inheritDoc}
     */
    public function reverse($latitude, $longitude)
    {
        return $this->geocode(sprintf('%F,%F', $latitude, $longitude));
    }

    /**
     * {@inheritDoc}
     */
    public function getName()
    {
        return 'google_maps';
    }

    public function setRegion($region)
    {
        $this->region = $region;

        return $this;
    }

    /**
     * @param string $query
     *
     * @return string query with extra params
     */
    protected function buildQuery($query)
    {
        if (null !== $this->getLocale()) {
            $query = sprintf('%s&language=%s', $query, $this->getLocale());
        }

        if (null !== $this->region) {
            $query = sprintf('%s&region=%s', $query, $this->region);
        }

        if (null !== $this->apiKey) {
            $query = sprintf('%s&key=%s', $query, $this->apiKey);
        }

        return $query;
    }

    /**
     * @param string $query
     */
    private function executeQuery($query)
    {
        $query   = $this->buildQuery($query);
        $content = (string) $this->getAdapter()->get($query)->getBody();

        // Throw exception if invalid clientID and/or privateKey used with GoogleMapsBusinessProvider
        if (strpos($content, "Provided 'signature' is not valid for the provided client ID") !== false) {
            throw new InvalidCredentials(sprintf('Invalid client ID / API Key %s', $query));
        }

        if (empty($content)) {
            throw new NoResult(sprintf('Could not execute query "%s".', $query));
        }

        $json = json_decode($content);

        // API error
        if (!isset($json)) {
            throw new NoResult(sprintf('Could not execute query "%s".', $query));
        }

        if ('REQUEST_DENIED' === $json->status && 'The provided API key is invalid.' === $json->error_message) {
            throw new InvalidCredentials(sprintf('API key is invalid %s', $query));
        }

        if ('REQUEST_DENIED' === $json->status) {
            throw new Exception(sprintf('API access denied. Request: %s - Message: %s',
                $query, $json->error_message));
        }

        // you are over your quota
        if ('OVER_QUERY_LIMIT' === $json->status) {
            throw new QuotaExceeded(sprintf('Daily quota exceeded %s', $query));
        }

        // no result
        if (!isset($json->results) || !count($json->results) || 'OK' !== $json->status) {
            throw new NoResult(sprintf('Could not execute query "%s".', $query));
        }

        $results = [];
        foreach ($json->results as $result) {
            $resultSet = $this->getDefaults();

            // update address components
            foreach ($result->address_components as $component) {
                foreach ($component->types as $type) {
                    $this->updateAddressComponent($resultSet, $type, $component);
                }
            }

            // update coordinates
            $coordinates = $result->geometry->location;
            $resultSet['latitude']  = $coordinates->lat;
            $resultSet['longitude'] = $coordinates->lng;

            $resultSet['bounds'] = null;
            if (isset($result->geometry->bounds)) {
                $resultSet['bounds'] = array(
                    'south' => $result->geometry->bounds->southwest->lat,
                    'west'  => $result->geometry->bounds->southwest->lng,
                    'north' => $result->geometry->bounds->northeast->lat,
                    'east'  => $result->geometry->bounds->northeast->lng
                );
            } elseif ('ROOFTOP' === $result->geometry->location_type) {
                // Fake bounds
                $resultSet['bounds'] = array(
                    'south' => $coordinates->lat,
                    'west'  => $coordinates->lng,
                    'north' => $coordinates->lat,
                    'east'  => $coordinates->lng
                );
            }

            $results[] = array_merge($this->getDefaults(), $resultSet);
        }

        return $this->returnResults($results);
    }

    /**
     * Update current resultSet with given key/value.
     *
     * @param array  $resultSet resultSet to update
     * @param string $type      Component type
     * @param object $values    The component values
     *
     * @return array
     */
    private function updateAddressComponent(&$resultSet, $type, $values)
    {
        switch ($type) {
            case 'postal_code':
                $resultSet['postalCode'] = $values->long_name;
                break;

            case 'locality':
                $resultSet['locality'] = $values->long_name;
                break;

            case 'administrative_area_level_1':
            case 'administrative_area_level_2':
            case 'administrative_area_level_3':
            case 'administrative_area_level_4':
            case 'administrative_area_level_5':
                $resultSet['adminLevels'][]= [
                    'name' => $values->long_name,
                    'code' => $values->short_name,
                    'level' => intval(substr($type, -1))
                ];
                break;

            case 'country':
                $resultSet['country'] = $values->long_name;
                $resultSet['countryCode'] = $values->short_name;
                break;

            case 'street_number':
                $resultSet['streetNumber'] = $values->long_name;
                break;

            case 'route':
                $resultSet['streetName'] = $values->long_name;
                break;

            case 'sublocality':
                $resultSet['subLocality'] = $values->long_name;
                break;

            default:
        }

        return $resultSet;
    }
}

@yurividal
Copy link

yurividal commented Jun 29, 2017

This is working fine. But, we should get Family Adress for people with no person_address.
Right now, it is returning only person_adress. so, people who have family address have blank on groupview @DawoudIO @phili67 (same for email, state, etc)

@crossan007
Copy link
Contributor

Is this still an issue?

@phili67
Copy link
Author

phili67 commented Jul 1, 2017

Yes it's an issue ;-).

Everything is translated and is working fine. The groupView was not working well.
I give you the point to make work

But you can see below that the adresses and cities (Ville) aren't present :
capture d ecran 2017-07-01 a 10 20 50

I investigate this point.

So the json doesn't give all the information :
capture d ecran 2017-07-01 a 10 35 19

So I went in your api : in api/routes/groups.php
The code isn't working well

    $this->get('/{groupID:[0-9]+}/members', function ($request, $response, $args) {
        $groupID = $args['groupID'];
        $members = ChurchCRM\Person2group2roleP2g2rQuery::create()
            ->joinWithPerson()
            ->findByGroupId($groupID);
        echo $members->toJSON();
    });

So I go forward in the code and find that this came from Person2group2roleP2g2rQuery :

public function joinPerson($relationAlias = null, $joinType = Criteria::INNER_JOIN)

I went forward too and went to : ChurchCRM/model/ChurchCRM/Base/person.php

If I use the function :

public function getAddress1()
    {
        return $this->per_address1;
    }

and change this to return : return "Crossan007";

I get a good result.

But after, I'm lost, it's to complex to debug, there's no way to debug classes in php.

It's possible to write myself a new code, but I think the poo model very good.

@phili67
Copy link
Author

phili67 commented Jul 6, 2017

I finaly find the solution : #2717

@crossan007 crossan007 modified the milestone: Backlog Jul 14, 2017
@DawoudIO
Copy link
Contributor

what is the status here

@crossan007
Copy link
Contributor

@phili67 can we close this?

@phili67
Copy link
Author

phili67 commented Dec 5, 2017

It's OK

@phili67 phili67 closed this as completed Dec 5, 2017
@DawoudIO DawoudIO modified the milestones: Backlog, 2.9.0 Dec 12, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants