Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion

osTicket v1.10 (stable) and Maintenance Release v1.9.15 are now available! Go get it now

Little showcase of my take on osTicket

Hey guys, I really learned a lot about php, html and mysql while using osTicket and this fantastic forum. I'd like to thank all the staff for creating such a nice product that is both easy to use and customize. I'm always having a good laugh when suddenly a funny comment pops up while scrolling through code.

I would love to give a bit back to you guys, not sure how capable I am though in helping you fit things into your environment. So I thought the best way would be to showcase the functions and design I added and if you think something would fit your system, just ask and I'll try my best to help.

Some things not visible: The inbox icon after the name serves as a dropdown button for unread messages or mentions. Staff can mention someone with @Name and he/she will get a notification in the dropdown menu (looks like the one from status change for example). The notifications are clickable and lead to the ticket where you were mentioned in or where there are unread messages.

The dashboard has a news section where our agents can add news items (again, an image serves as a button for a dropdown menu). If you're on the ticket page and someone posts news, another notification will pop up in your inbox.

Code for the refresh is really ugly at the moment though, it's basically existing two times in tickets.php and for it to refresh with the set ticket page refresh time from osTicket. So if someone could give me a hint how to handle that better, I would really appreciate that. If I take it out of, the inbox will stay empty on all pages except tickets.php, that's why I put it there in the first place (should probably be in header.php though, right?)

Cheers, Martin



  • I'll pass this thread along to the devs to take a peek

  • Hey,

    Love the design and also the idea of being able to mention agents.
    Have you made an autocomplete like the variables for the mention?

    What refresh is it you're talking about?
    For the mention to pop up as soon as it's made?
    The built in one for the ticket list?
  • edited May 2017
    Thank you so much ntozier, that's awesome! :)

    Hey Micke1101, thank you for the compliment!
    That's a great idea, I haven't thought about that yet. We are a small team of 11 people and the @-mention only needs the first name of the agent, so it wasn't really necessary. But for bigger staffs it would totally make sense.

    Yeah, I would love to refresh the header together with the tickets page (the built in one that every agent can configure). That's enough for our environment at least. Again, the new message popping up right now would be totally cool, especially when a lot of new messages are coming in.

    Edit: Forgot to mention that the whole header up to the subnav has fixed positioning and gracefully floats on top, so that you can quickly switch to the knowledge base for example or open a new ticket even if you're at the bottom of your 50 tickets page (or a long ticket thread).
  • Just a little addition, this is what the inbox looks like:


    Hovering over the items of the list displays the full text if it got truncated, clicking on them leads to the corresponding ticket and clears the notification.
  • edited June 2017
    How are you storing the information about what mentions i've read and not read?
    Is it possible to mention teams and or departments?
  • A social ticketing addon may be a great idea. 
  • It makes it so much easier to keep track of new answers and inform colleagues about something in a ticket without having to assign it to them. It also gives our colleagues the choice of either checking mails or keeping the ticket system tab open (the tab title also changes when there are unread messages).

    It is a bit dirty at the moment, since I am not that comfortable in MySQL and PHP yet.
    Basically added a column "isreadby" to the ticket table and the thread table.
    If you for example get mentioned the query looks for entries in the thread table where "body" contains @$thisstaff->getFirstName and "isreadby" is either empty or does not contain $thisstaff->getId.
    The queries also check if the ticket is either assigned to you or one of your teams.

    If I get some time on my hands I would love to redo it and use a comparison table instead of having so many NULL values (because of old closed tickets).
    That would make it easier to include teams and departments for mentions.
  • Yea I had a similar idea when i saw your thread in structure, but I think such a query can turn quite heavy in the future if you've alot of tickets, maybe something like this could work?
    [New table]
    mention_id, entry_id, object_type, object_id, isread

    So you can have multiple types of mentions, quicker search on larger databases.

    Also I know that it's kinda against the topic but maybe there should be a button to make all mentions read, maybe you don't have time to read all and/or you've already read it, but it didn't register.
  • Yeah I would make the structure pretty similar to what you suggested.
    Probably like id, object_type, object_id, staff_id and maybe team_id.

    Have it insert a new line on a new message or mention and delete it when the agent has read it. That way the table would stay pretty small and only has entries for current notifications instead of every ticket and thread having the isreadby row with entries no matter if it's been read or not.

    The button is on my todo list as well :D But that would probably be much easier with the new table intact.
  • Hey guys, just a little update on this. I pretty much recreated everything with a new table.
    It is much better than the old system, I have to say. Shame on me for not doing it this way from the start.

    Also made implementing the "mark all read" button super easy.

    I do have one problem though. It works well on messages the clients send through the frontend. When they answer in reply to the mail though, no new row is created.
    Currently I have the code under "function onMessage" in class.ticket.php. As I said, this works for frontend messages but not mails. Does anyone have an idea where to put the code to make it work?
    Maybe somewhere in class.mailparser.php?
  • edited June 2017
    You might want to use the this function instead, it's what is called upon by both postEmail and postMessage

    Or if you want to include other things than messages aswell

  • @Micke1101
    Thank you for the hint, I tried it but to no avail.

    This is the code that works under function onMessage:

    if ($this->isAssigned()) {
                $thisticket = $this->getId();
                $assignedStaff = 0;
                $assignedTeam = 0;
                $members = [];
                if ($this->getAssignee() instanceof Staff) {
                    $assignedStaff = $this->getAssignee()->getId();
                    $insert_unread = mysql_query("INSERT INTO ost_unread (object_type, object_id, staff_id, team_id) VALUES ('R', '$thisticket', '$assignedStaff', '$assignedTeam')");
                if ($this->getAssignee() instanceof Team) {
                    $sql = mysql_query("SELECT staff_id from ost_team_member WHERE team_id='".$this->getTeamId()."'");
                    while($get_array_sql = mysql_fetch_assoc($sql)) $members[] = $get_array_sql['staff_id'];
                    foreach ($members as $member) {
                        $insert_unread = mysql_query("INSERT INTO ost_unread (object_type, object_id, staff_id, team_id) VALUES ('R', '$thisticket', '$member', '$assignedTeam')");

    Please don't laugh at it, I still am a noob regarding php and mysql.. :D I hope it is understandable though.

    The problem is probably that I can't get any of the functions to work since the object is thread and not ticket, right?
  • Yea you're correct, you might want to check if it's a task and or a ticket aswell.
    Use the following as a guideline before your code and then just replace $this with $ticket.

    $thread = $this->getParent();
    if($thread->getObjectType() == 'T') {
        //Ticket specific code
        $ticket = $thread->getObject();
    } elseif($thread->getObjectType() == 'A') {
        //Task specific code
        $task = $thread->getObject();
    } else {
        //Unknown type
  • Thanks for the tip @Micke1101
    I tried a lot around yesterday but could not get it to work.
    I put my code right after the
        $ticket = $thread->getObject();
    since we only have tickets right now. I saw something confusing though: It threw a php error saying getObjectType() was run on null value. I took a look at the thread_entry table and the pid column only has rows with value 0. Is that even correct? Or should pid be equal to the ticket the thread belongs to?

    I am also confused since in the onMessage function, my code only works if I put it after the initial $this stuff. If I put it at the end of the function, it doesn't work at all.
    My code also works perfectly under the onAssign and onNewTicket functions, for BOTH frontend and mail tickets..

    I am sorry for the many questions but I hope you can help me.
  • Hey @Grizly sorry to mention you out of the blue but I saw your post in the signal thread and thought maybe you can help me with this issue? You would totally make my day if you could take a look.
  • edited June 2017
    Have you a repo?

    One thing that might work, is connecting a signal via: 'threadentry.created',  the ThreadEntry::create function sends one when when finished, passing the whole entry object.

    That $entry object should have the ThreadEntry functions you need: getStaffId(), getThreadId(), however it doesn't have any getTicketId().. which makes it hard to work with. However, with the entry_id you can find the ticket. Once you've a ticket object, all bets are off!

    Something like this in your plugin:

    function bootstrap() {
    Signal::connect ( 'threadentry.created', function ($entry) {
    $ticket_id = Thread::objects ()->filter ( array (
    'id' => $entry->getThreadId () 
    ) )->values_flat ( 'object_id' )->first () [0]; //YMMV, tested on PHP7
    $t = Ticket::lookup($ticket_id);

    // Do your thing
    die ( "Tests mate, ticket ID was $ticket_id" );
    } );

    Not the most elegant, but it avoids having to hack core, which I'm in favour of. 

  • Hey @Grizly,
    thanks for your fast response! Sadly it is neither a plugin nor do I have it on gitHub (yet, I would love to share this via Plugin with the community).

    So I took a look at your code and looked for the corresponding Signal::send in class.thread.php since I wasn't sure where to put your code to make it work. I found it in the function create of the ThreadEntry class and put my code right before the Signal::send (together with the code you provided to get the ticket_id from $entry, that was very helpful!):

            $thisticketid = Thread::objects()->filter(array('id'=>$entry->getThreadId()))->values_flat('object_id')->first()[0];
            $thisticket = Ticket::lookup($thisticketid);
            $assignedStaff = 0;
            $assignedTeam = 0;
            $members = [];
            if ($thisticket->getStaffId()) {
                $assignedStaff = $thisticket->getStaffId();
                $insert_unread = mysql_query("INSERT INTO ost_unread (object_type, object_id, staff_id, team_id) VALUES ('R', '$thisticketid', '$assignedStaff', '$assignedTeam')");
            if ($thisticket->getTeamId()) {
                $sql = mysql_query("SELECT staff_id from ost_team_member WHERE team_id='".$thisticket->getTeamId()."'");
                while($get_array_sql = mysql_fetch_assoc($sql)) $members[] = $get_array_sql['staff_id'];
                foreach ($members as $member) {
                    $insert_unread = mysql_query("INSERT INTO ost_unread (object_type, object_id, staff_id, team_id) VALUES ('R', '$thisticketid', '$member', '$assignedTeam')");

    ...and I thought, hey, this could totally work. But nah, again this only works for Frontend and SCP messages but not mails, what the f... :(
    Also tried the same code under the postEMail function, but same result.

    I really don't get it. Why does it work perfectly for everything but mails?
  • Hmm, that makes no sense, the mailparse/mailfetch tasks should all end up using a ThreadEntry object to process all entries in a thread, they all eventually call that function which sends that signal.. well, unless something triggers one of the many "fail" points. Is it getting there and failing? Or not even getting there?

    It works on mine, for new email and replies to existing tickets.. Is your code only doing things to tickets that are assigned? Because new email won't have an Agent/Team.. (well, unless your filters are doing that), so try it with a reply.

    How are you debugging the failure? What type of mail interface are you using? (fetching/piping), is the message being denied, a new ticket created, the thread-entry working fine but your code not running? What type of failure are you seeing? Do you have any logs? (admin/web etc).  

    If you're polling/fetching mail, and calling cron to fetch the mail, then you can use simple print_r/vardump stuff and run it manually instead, I like to set "move mail to folder" in my settings, then just delete the tickets, move the mail back and run it again, it lets me test things over and over. 

  • edited June 2017
    Sorry for the late answer, took a day off on friday.
    Just for clarity: The code is supposed to work only on assigned tickets or else it would get a bit messy. If the ticket isn't assigned to oneself, there is always the option to mention someone for a notification.

    So I put my code right after the definition of $entry so it would be called immedietly. But still the stuff only gets executed on frontend entries. For debugging purposes I put the insert query even before the first if so it would ALWAYS insert a row into the table but this doesn't happen on mails. On frontend answers or new tickets it works fine and inserts both a row with R / ticket_id / 0 / 0 and R / ticket_id / assigned_agent_id / 0.

    The debugging is what drives me crazy since I am quite the noob at php and mysql. There are no errors in the php_error.log.. Everything is working fine as well, via mail tickets get created or messages appended just fine.  But the code is not called for whatever strange reason. This also makes it very hard for me to understand the error. We fetch the mails every minute via IMAP+SSL by the way (using cron in the task scheduler).

    Would you be so kind and explain the last part a bit more? Where to put the vardump and on which variable?
    Thank you so much for your time!
  • Share the code mate, I don't know what "the stuff" is that's getting executed, or rather, isn't getting executed.

    What do you mean "immediately after the definition of $entry"? You mean the first assignment to the variable in the ThreadEntry::create function? (new static())? why..? Why not wait till it's actually ready?

    Debugging via dump? 
    Well, you can't really step through osTicket, because it's hideously complicated and relies on external things like email and cron (which you don't want to break in prod), so to dump things, just vardump or print_r them and exit, when you run cron manually (/usr/bin/php /osticket/api/cron.php) you'll see everything you dumped on the command line.. it's not great, but it works.

    The reason I go out of my way to make things plugins now, is because of updates. You'll have to rewrite everything every time you upgrade, and so will anyone using your code
    Handily, anything can send signals whenever you want, however you want.. with whatever you want! It's pretty good. 

    Signal::send('just fuckin work ya bastard', $thread);

    So, edits to core become just that, sending an object via a signal to your plugin. Much easier. Also, it gives you boilerplate config and such. 
  • I am so sorry, but I'll have to do take the "easy" way for now to just make it work since people got used to it working and it is just so handy to keep track of new messages without having to look out for mails or thread message count. At the moment, I am also missing a lot of new responses myself since our customers often answer via mail.

    For now, I just put two little sql queries into an extra php file which gets called from the tickets.php to make it update on every refresh. One for the mentions and one for unread messages. The results then get exported into the ost_unread table. They work perfectly, no matter if new stuff was send via mail or frontend.

    You made me very curious though and I'll try recreating everything as a plugin with signals in my spare time to make this available for everyone that would like to use this plugin. Found a nice guide (even though it is a bit dated) in another thread on plugin creation that made me realize I have to learn a lot about php basics first though.

    Thank you so much for your time, tips and hints @Grizly. I hope you can understand. I will try to continue this project as soon as there is some free time on my hands.
  • edited June 2017
    No worries mate, It's an interesting idea, and I'm keen to see it work. Not so much for me or mine, we just use collaborators, I like the lazy/simple idea of @YouThere being adding as a collaborator, which would trigger normal alerts/notices etc. In fact, just written a plugin to do that:

    Obviously nowhere near as pretty as yours. :-)
  • @Grizly how do I add this plugin to my installation, this is a brillaint idea
  • I've published a release which is working on mine, you do need to know their username, or have only one name for each Staff member.. it get's a bit weird if you @mention someone who isn't staff.. lol

  • edited July 2017
    Hey @MartinLKP


    I've been working with @Sam_Parris to make this plugin a lot better, have a look mate, you might be able to fork it to make yours a plugin! 

    We're keen to see how you've gotten on as well. 

    I'm concerned that yours wasn't working via email, have you fixed that?
  • edited December 2017
    Whoops, it has been quite a while :(

    I did not find the time to make it a plugin yet and am afraid I never will. But you did an awesome job at putting the core components into the plugin! Great work. And thanks for the credit for the idea ;)

    I rewrote some stuff and also put it into an extra php and am now calling it from tickets.php to refresh. I never gotten the other methods to work so I stuck with the working sql queries.

    I added quite some stuff though, a follow system connected with the @-mention so you dont always have to do it or for when there are tickets you're interested in updates.

    Also rebuild the news section in the dashboard area, added a little calendar area and a ticket overview area. I also added an action timeline to the ticket overview and rebuilt the ticket details page. Left side is fixed position and always reachable. The red line is just an indicator of how much time has passed relevant to the due date.

    Since we aren't available all the time but get calls nontheless, I coded an agent status bar as well that sits in the middle of the header with selectable status and status note option.

    Maybe someone is interesting in any of this. If so, you can PM me and we can work it out I guess. It's all core files editing though and I can't guarantee it to work in your environment.
    Maybe we will find some time @Grizly and can work some of this out into a plugin for everyone to use. Just if you are interested as well though ;) You have the plugin knowledge and I have the "dirty" code that works :D
  • @MartinLKP Are we able to download your awesome customization ? =)

Sign In or Register to comment.