Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
nielsm
Jun 1, 2009



Laravel 5:

I'm building a thing and learning the framework as I go. I've just started implementing authorization with the built-in system, with Policy classes, which is pretty neat for permissions tied to specific model instances. But what when I have a permission not tied to any model instances, but is rather global, so to say?
The specific case I have right now is "create new teams", only some users should have that permission. I can't check the permission on an object that doesn't exist yet, how are you supposed to declare that?

Adbot
ADBOT LOVES YOU

nielsm
Jun 1, 2009



DarkLotus posted:

You'll check against the user object. So tie that ACL to the user object to see if the user is allowed to create teams.
Something like this:

So essentially use a dummy parameter when nothing else is appropriate? Kinda lovely solution but I guess I can live with it.

nielsm
Jun 1, 2009



Bob Morales posted:

Line 239 it works, 245 it doesn't. I would guess it might be something in the variable that gets passed in. Notice that it's not escaped, I should do a SQL injection test :cry:

Line 239 and 240 seem to me to be the broken ones? Based on the example output.

Maybe try copy-pasting the whole "<br>\r\n" string from a working line to a broken one, to fix if there may be some invisible Unicode characters or whatever in the middle.

Otherwise try var_dump()'ing the strings, that should also reveal if they have odd things in them.

nielsm
Jun 1, 2009



LP0 ON FIRE posted:

I'm trying to attach a pdf from a URL. All it sends is an empty file. I verified that the filename and path are valid. I've tried a few variations, including using curl, and I have the feeling that maybe is something is going wrong in how the headers or body is written?

Does the file you're sending really only exist on a remote server? I.e. what you do is download the file for the user and send it by mail? (If the file you want to send exists on your own server, you shouldn't be passing a HTTP URL to file_get_contents(), but just the filename.)

Does the file download if you fetch that URL using your browser? Using a commandline tool (like wget or curl)?

Why does your (original) code write the downloaded data to a new file, only to immediately read it back? Do you know if that writing the file works or fails?

nielsm
Jun 1, 2009



awesomeolion posted:

Any idea why my time is off here by 46 minutes?

code:
date_default_timezone_set('America/Los_Angeles');
$date= date('Y-m-d H:m:s');
echo "<h1>Current time: " . $date . "</h1>";


You want "i" for minutes, "m" means month.

nielsm
Jun 1, 2009



The basic thing is that you are calling add_word() incorrectly, you pass a stringified version of $_POST which is useless, instead of just passing it directly.

This is wrong:
code:
add_word("$_POST");
This is better:
code:
add_word($_POST);
But the function ought to have a different signature altogether:
code:
add_word($_POST['word']); // passing just the data needed, instead of an array full of other junk that may or may not be needed
Also, you should never, ever use the extract() function.

You should also use parameterized queries instead of munging values and pasting them into SQL strings directly.

nielsm
Jun 1, 2009



Oh right, $connection in that mysqli_query call, that's probably a global. If it is you need to declare it as such at the start of the function body.

http://php.net/manual/en/language.variables.scope.phpv

nielsm
Jun 1, 2009



Sulla-Marius 88 posted:

Is there a function or sample code that can exactly reproduce javascript's deprecated escape() function? I know it's deprecated, but our partner won't update their code so we need to replicate it in PHP. I've tried googling but every response is just people saying "don't use deprecated code" and unfortunately we don't have that luxury.

Untested:

PHP code:
function js_escape($str) {
  $fn = function($matches) {
    $string = $matches[0];
    $offset = 0;
    $code = ord(substr($string, $offset, 1));
    if ($code >= 128) {        //otherwise 0xxxxxxx
      if ($code < 224) $bytesnumber = 2;                //110xxxxx
      else if ($code < 240) $bytesnumber = 3;        //1110xxxx
      else if ($code < 248) $bytesnumber = 4;    //11110xxx
      $codetemp = $code - 192 - ($bytesnumber > 2 ? 32 : 0) - ($bytesnumber > 3 ? 16 : 0);
      for ($i = 2; $i <= $bytesnumber; $i++) {
        $offset ++;
        $code2 = ord(substr($string, $offset, 1)) - 128;        //10xxxxxx
        $codetemp = $codetemp*64 + $code2;
      }
      $code = $codetemp;
    }
    if ($code < 256) {
      return sprintf("%%%02X", $code);
    } else {
      return sprintf("%%u%04X", $code);
    }
  }
  return preg_replace_callback("|[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./]|u", $fn, $str);
}
Based on spec at http://www.ecma-international.org/ecma-262/6.0/#sec-escape-string and sample from http://php.net/manual/en/function.ord.php#109812.
This "should" handle Unicode within the BMP, and fails with characters outside the BMP.

E: The best way to support non-BMP characters may be to first convert the input string to UTF-16 and then process that. It may even be shorter code.

nielsm fucked around with this message at 12:36 on Feb 10, 2017

nielsm
Jun 1, 2009



funny Star Wars parody posted:

Hey guys, more dumb noob questions and I will admit that a lot of it involves javascript so if it's better suited to that thread feel free to let me know. I'm trying to make it so that a user can select files that they wish to download or delete from the server (this is an offline embedded device with a lighttpd instance running so that the client has a pretty file management interface) and I can't figure out how exactly to tell the php what to do when the user clicks a button (either download the selected files as a zip or delete them).

From this snippet, it looks like you're generally confused with order of execution, and what executes on server and what on client, and how to share data between the two.

HTML code:
                            <a href="#" onClick="toDelete()">
JavaScript code:
                        function rec(deletestuff) {
                            $('#newCode').load('delete_selected.php' deletestuff);
                        }
Here you first have a link that wants to call a JavaScript function named toDelete.
Right after that you define a JavaScript function named rec, which also happens to have a syntax error.
Are the two supposed to be related? Is the rec function actually supposed to be toDelete?

The syntax error is that you just stick 'delete_selected.php' deletestuff together like that, you need an operator (probably +) between them, except that would probably end up as an invalid URL. It's not clear what deletestuff would contain, from the parts you pasted.

HTML code:
                                        <input type="checkbox" name="selected[]" value="'.$file.'">
This is not going to paste any filenames or otherwise in, you're missing some PHP start/end tags around $file.

JavaScript code:
                            $('input[type=checkbox]').each(function () {
                                if(this.checked) {
                                    toDelete.push(this.id);
                                }
                            });
This snippet is run unconditionally as part of the page load. It's not in any event handler, so it would get run exactly once, and just add the it's of any initially checked boxes to the toDelete array.
Which brings me to, suddenly toDelete is an array, but you were using it as a function above.


I think the first thing you should consider is whether you actually need to use JavaScript at all. In your post in the general programming questions thread (which reminded me of this, I had intended to reply earlier but forgot about it) you mention that this thing will run locally only on a small computer, not over the internet. In that case you know something about the performance, you know there will only be a single user at a time, and you know that transfer speed and latency between client and server are both as perfect as they can be. So you don't need to use client side scripting to mask over the performance of regular network connections.

The much simpler solution, which may be what the original thing you're modifying already does, is to just have a plain old HTML form, no JS, you submit, which then batch deletes all the checked off files, and returns a new page with a new directory listing. Optionally with a log at the top/bottom about changes made.
If you really do want a dynamically updating view, you need to completely rework this. You should probably make an array of plain old checkboxes with no events attached. Then have a button you attach a JS event handler to, that event handler then finds all checked boxes, collects the data into a form submission format, uses an XMLHttpRequest to call a PHP script on the server, and waits for then processed the response. The PHP script it calls should accept the form input generated by the JS, process the files, and return a JSON object containing status for each element the JS requested things to be done for. When the JS receives this response, it can go over the returned JSON, find the checkboxes for the files successfully deleted, and replace those checkboxes with a message that the file was deleted, or any error that maybe occurred.


Edit: I made a sequence diagram.

nielsm fucked around with this message at 09:27 on Apr 8, 2017

nielsm
Jun 1, 2009



Gozinbulx posted:

secondly, if i DID successfully get someone to make the feature I want, is it considered untoward to propose a pull request based on a hired gun's code? Does that violate some GPL/Open source poo poo? Not that it really matters to me all that much if it works for me, but I do know there are people in the Git who want this feature so I'm wondering how the dev would feel.

You will want to make publication or not of the modifications part of your contract with the developer. As far as I can tell, SeedDMS is GPL 2 license. I am not a lawyer, however my interpretation is that any modified or extended version must also be covered by GPL or a compatible license on 100% of the code. That in turn means anyone who receives a copy of the modified program is entitled to do whatever they want with it (including redistributing it to anyone they wish) with full source code, as long as all authors are properly credited. On the other hand, it also means that as long as you do not give anyone else a copy of the modified program, nobody else is entitled to it either. (Although I think you also couldn't stop the hired developer from running with the modifications.)

The most courteous would be to hire a developer who will work with the open source project and have your required modifications become part of the public distribution. If it's something major you can perhaps negotiate having your business mentioned as a sponsor on the project website.

nielsm
Jun 1, 2009



Agrikk posted:

Can someone point me to a good resource for learning how to batch insert items into a MySQL database using PHP?


I have a tab-delimited file of 1.1 million rows that I want to insert into a MySQL database (it's actually AWS Aurora) every hour. Most of the examples I find online are of people using arrays to receive the data from the flat file and then using loops to push the data into the database. But iterating over a million-member array one at a time seems dumb and inefficient.

Prepared statements and working inside a transaction should be the basic approach. Prepare your insert statement once, then execute it multiple times with different data. Work inside a transaction to allow the database to not flush to disk constantly. (But for some resumability it may be a good idea to commit the transaction every so often, say every 100 or 500 rows.)

MySQL also supports inserting multiple rows in a single insert statement, using the syntax INSERT INTO table (field1, field2, ...) VALUES (a1, a2, ...), (b1, b2, ...), (c1, c2, ...), ...;
It may have better performance to batch multiple input rows that way, e.g. have your prepared statement process 10 rows at a time.

nielsm
Jun 1, 2009



ConanThe3rd posted:

So, I have a client who wants to take their Excell spreadsheet of ID Numbers and copy-paste it into a textfield to apply a status to it.

Currently I have explode() doing that but I'm not sure what character I use for a new row for excell, any help?

If you implement a rich-text editor (i.e. a contentEditable HTML thing), copy-paste from Excel will usually end up as an actual HTML table you can traverse client side with DOM, or submit and parse as HTML.

nielsm
Jun 1, 2009



The really proper way would be to have a separate service/daemon running on the server, which accepts jobs posted via a message queue, or maybe just inserted into a database table. How to actually set up something like that depends wholly on the hosting environment, and most cheap webhosts won't support this scenario at all. (Cheap webhosts will also kill any long running processes spawned by you, so an exec() solution would likely break the same way.)

nielsm
Jun 1, 2009



At what point is your environment variable getting set?

As far as I understand IIS architecture, you run IIS as a system service, which does not inherit any environment, and IIS in turn runs workers such as PHP FastCGI in an application pool, which does not inherit any environment either. So you'd have to configure the environment at the application pool level.

nielsm
Jun 1, 2009



Jeffrey of YOSPOS posted:

Lol I am personally sorry to this thread that [php] tags look bad in dark mode. I did work to make dark mode play nice with [code] tags but [php] tags use a completely different mechanism that is harder to change dynamically. I'm working on it and this is very silly.

Can the [php] tags simply be translated to [code=php] instead?

nielsm
Jun 1, 2009



The file should be closed when the request finishes, regardless of you closing it explicitly, but it's still good practice to explicitly close your handles.

nielsm
Jun 1, 2009



Two things:
You only have one instance of the countdown JS, and it only handles a single $countdown value.
And it looks for the cell to fill in the countdown by HTML "id", which is by definition unique inside the document, it's not allowed to have two HTML elements with the same "id".

What I'd personally do is make an empty cell in the output HTML from the PHP, give those cells a specific HTML "class", and then use a "data" attribute on them to store the countdown target. Then in the JS, you can search for all elements with the specific "class" value and create timers for each of them.

PHP code:
while($row = $result->fetch_assoc()) {
  echo "<tr>";
    echo "<td class='tg-dzk6' width='200 px'>", $row["creator"], "</td>";
    echo "<td class='tg-dzk6' width='200 px'>", date("H:i D d M Y e", $d_c), "</td>";
    echo "<td class='tg-dzk6' width='200 px'>", date("H:i D d M Y e", $d_r), "</td>";
    echo "<td class='tg-dzk6 countdowncell' width='200 px' data-expirytime='", $d_e, "'>"; # ----- THIS LINE -----
    echo date("H:i D d M Y e", $d_e); # just for some contents while the JS starts up
    echo "</td>";
    echo "<td class='tg-dzk6' width='200 px'>", $row["comments"], "</td>";
  echo "</tr>";
}
JavaScript code:
let countdown_cells = document.getElementByClassName("countdowncell");

countdown_cells.forEach((td) => {
  setInterval(() => {
    let remaining = td.dataset.expirytime - Date.now();
    td.innerhtml = remaining; // TODO: format this nicely
  }, 1000);
});
The JS code is only needed once, at the very end of the page, since it searches for all the cells and creates all the timers in its own loop.

nielsm
Jun 1, 2009



Nah you almost certainly want to check the actual client system time every time the interval timer runs in JS. You don't have any guarantee the timer runs exactly every 1000 ms, it can run a bit later and eventually you get noticeable timer drift. It can be okay to pass the server timestamp in from PHP, but you still want to at least calculate a time delta between page load and now with JS.

nielsm
Jun 1, 2009



"meta_data" is an array of objects, you need something like $item_data['meta_data'][0]['value']['deposit'] or probably rather a loop over it, in case there's multiple metadata.

nielsm
Jun 1, 2009



Don't use PHP to send the file.

Or reinvent the wheel and implement a chunked reading and transfer where you continually check connection_aborted() as well as handle HTTP range requests in full.

nielsm
Jun 1, 2009



Not better at all.

You need to be checking the connection_aborted() function whether the client has disconnected and then stop sending, otherwise you are definitely wasting server resources sending something to nowhere.

Sending content type application/octet-stream is a good way to ensure that browsers will almost certainly not use the video in a <video> element. Send the right MIME type for the data unless you explicitly want it to only be a file download.

If the goal is to let browsers play the video you really should also implement range requests as I suggested above. That can allow seeking in the video without downloading the full thing.

I would also suggest using a safer identifier for the data to send than just a filename. Have a database or something to look up that the requested resource is on the approved list, and optionally do other permission checks for the specific client/user. Otherwise you're far better off just serving the files directly via your web server instead of wrapping the download in a script.

Edit:
... I just noticed. You're taking the video file from a web URL and re-sending it? Is that on the same server, or on somewhere else?
Just to be sure, you do realize that what you're doing there is tell PHP to open a HTTP connection as a client to the URL you construct there, download the data at that URL, and then re-send the just downloaded data to the client that script is serving, right?

nielsm fucked around with this message at 22:12 on Apr 23, 2022

nielsm
Jun 1, 2009



Jabor posted:

Alternatively, if you're just trying to store this information and aren't interested in querying based on the values in it, storing all the attributes in a single JSON field will be much easier to work with.

Going to second this. JSON fields can be useful when you mostly just need to dump some data for storage.
You can still query on them, it's just horrible and slow.

nielsm
Jun 1, 2009



Do you allow one pair of nodes to have multiple connections between them? (Or must there be at most one connection between any pair of nodes?)
Do you allow nodes to connect to themselves?
Do you allow the graph to have multiple "islands"? (Disconnected sets of nodes, where there is no possible path from a node in one island to a node in a different island.)
Do you allow any nodes to have zero connections (be a single-node island)?

The more "no" you have in the above, the harder the solution will be.

On the other hand, if you can say "yes" to all of the above, one way to do it is to determine the number of desired connections for each node ahead of time, put every node into an array as many times as it needs connections, shuffle that array, remove the two last nodes in the array and connect those with each other, repeat until you've exhausted the array.

nielsm
Jun 1, 2009



If you're okay with getting Python code in the PHP thread, I tried writing something that generates a graph and at least tests whether it fulfills the entirely connected requirement.

Python code:
import random

class Node:
    def __init__(self, index):
        self.index = index
        self.edges = set()
        self.color = None

def allow_connection(n1, n2):
    # only allow connection if nodes are not already connected
    return n2 not in n1.edges and n1 not in n2.edges

def initial_connect_nodes(count, min_connections, max_connections):
    # make initial array of nodes
    nodes = [Node(i) for i in range(count)]
    # make a list of a random number of references to every node, one ref for each connection desired from that node
    to_connect = [node for node in nodes for _ in range(random.randint(min_connections, max_connections))]
    # shuffle the connection order
    random.shuffle(to_connect)
    # ensure that there is an even number of nodes, since we need to connect them in pairs
    if len(to_connect) % 2 == 1: to_connect.append(to_connect[0])
    # generate connections
    for i in range(0, len(to_connect) - 1, 2):
        # pick the front two nodes in the list
        n1, n2 = to_connect[i], to_connect[i+1]
        # check if the nodes are allowed to connect, find another node if not
        j = i + 1
        while not allow_connection(n1, n2):
            j += 1
            # fail if we run out of nodes (none of the remaining nodes are allowed for connection)
            if j >= len(to_connect): return None
            # swap current n2 candidate backwards in the array and pick a new one
            to_connect[i+1], to_connect[j] = to_connect[j], to_connect[i+1]
            n2 = to_connect[i+1]
        # connect the nodes
        n1.edges.add(n2)
        n2.edges.add(n1)
    return nodes

def colorize_nodes(nodes):
    # wash the colors off
    for n in nodes:
        n.color = None
    # begin painting
    nextcolor = 0
    for start_node in nodes:
        # if node is already colored, don't process
        if start_node.color is not None: continue
        # otherwise assign the next available color to it
        start_node.color = nextcolor
        nextcolor += 1
        # now visit all reachable nodes and paint them too
        stack = list(start_node.edges)
        while len(stack) > 0:
            # grab one node from the stack
            n = stack.pop()
            # if it's already painted skip it
            if n.color is not None: continue
            # otherwise paint it
            n.color = start_node.color
            # and push all nodes connected to it too
            stack.extend(n.edges)
    # the nextcolor variable will also be the count of unique colors assigned
    return nextcolor

nodes = None
tries = 0
while nodes is None:
    tries += 1
    nodes = initial_connect_nodes(10000, 1, 20)
num_colors = colorize_nodes(nodes)

print(f"Generated initial graph with {len(nodes)} nodes in {tries} tries, it has {num_colors} colors")
#for n in nodes:
#    edgeindexes = ', '.join(str(e.index) for e in n.edges)
#    print(f"Node {n.index} (color {n.color}) is connected to nodes: {edgeindexes}")
This generates a graph in 0.1 seconds on my machine, and about 25% of the time that graph will be entirely connected, so you can just re-run it until you get something usable.
Otherwise, it would be possible to write some algorithm to attempt to connect the disconnected islands too, probably something like pick two islands, select the least connected nodes in each island, and connect them pairwise, and repeat until the entire graph is connected.

nielsm fucked around with this message at 23:08 on Feb 26, 2023

nielsm
Jun 1, 2009



Agrikk posted:


Which now raises another question: How to write the code to walk this mesh, finding the most direct path between two randomly chosen nodes?

Look up "single source shortest path algorithm", there are multiple famous algorithms for it.

nielsm
Jun 1, 2009



You can also structure it as a lookup table, where each item stores the range start, range end, and the mapped values, then search the mapping by value. Or if all the search values are integer, use an array with the lookup key, so e.g. indexes 1 up to 20 in the lookup array all reference the same mapping object. If you need to translate a large number of values that's probably an advantage performance-wise.

Adbot
ADBOT LOVES YOU

nielsm
Jun 1, 2009



You can clone the original repository locally and checkout the relevant branch or tag. Then add the other repository that the Pull Request is sent from as a second remote to your local checkout, and fetch it. Then you should have the branch the pull request is from as a reference that you can merge in.

So it becomes something like:
code:
git clone https://github.com/Vendor/Product
cd Product
git checkout release-branch
git remote add contributor https://github.com/contributor/Product
git fetch contributor
git merge contributor/feature-branch

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply